pirl-2.3.8/0000755000175000017500000000000012052711374012346 5ustar mathieumathieupirl-2.3.8/PIRL/0000755000175000017500000000000012052546527013122 5ustar mathieumathieupirl-2.3.8/PIRL/Build/0000755000175000017500000000000012052546513014154 5ustar mathieumathieupirl-2.3.8/PIRL/Build/Copyright_Notice.GPL0000644000175000017500000000201511742731557020000 0ustar mathieumathieu /******************************************************************************* Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ pirl-2.3.8/PIRL/Build/Tag0000755000175000017500000000650511615642624014627 0ustar mathieumathieu#!/bin/csh # # Tag # # Use: # # Tag [-Verbose] -Tag # # Description: # # All files from the CVS respository in each package hierarchy have # their checked-in revision tagged. The tag format is: # # - # # where is the user specified -tag and is the version # number obtained from the VERSION file with all '.' characters # replaced with '_' characters. # # If -verbose is specified all files tagged will be listed, otherwise # only the total number of files tagged in each package will be listed. # # A file can not be given the same tag twice. Thus Running Tag again # without a change to the VERSION number or -tag name will only affect # files that have been added and committed to the CVS repository since # the previous Tag. # # -Help # # List the brief usage description. # # Author: # # Bradford Castalia, UA/PIRL # # CVS ID: Tag,v 2.1 2011/08/02 00:32:20 castalia Exp set procedure = `basename $0` while ($#argv) switch ($argv[1]) case -[Tt]*: shift if ($?Tag) then echo "${procedure}: Multiple tag names specified -" echo " $Tag" echo " $argv[1]" goto Usage endif if (! $#argv) then echo "${procedure}: Missing tag name." goto Usage endif set Tag = $argv[1] breaksw case -[Vv]*: set Verbose = 1 breaksw case -[Hh]*: Usage: echo "Usage: $0 [-Verbose] -Tag " echo ' Tag format: -' exit 1 breaksw case -*: echo "${procedure}: Unrecognized $argv[1] option." goto Usage breaksw endsw shift end if (! $?Tag) then echo "${procedure}: No tag specified." goto Usage endif # Check for completness. set version = (`grep '^[0-9]' VERSION`) set version = `echo $version[1] | tr . _` if ("$version" == "") \ set Missing_VERSION = 1 set Directory = ".." set Packages = \ ( \ Build \ Conductor \ Configuration \ Database \ Image_Tools \ Messenger \ PVL \ Strings \ TreeTable \ Utilities \ Viewers \ ) set Missing_package = () set Missing_CVS = () foreach package ($Packages) if (! -d $Directory/$package) then set Missing_package = ($Missing_package $package) else if (! -d $Directory/$package/CVS) \ set Missing_CVS = ($Missing_CVS $package) endif end if ($?Missing_VERSION) then set Incomplete echo "${procedure}: Missing VERSION" echo endif if ($#Missing_package) then set Incomplete echo "${procedure}: Missing packages -" foreach package ($Missing_package) echo " $package" end echo endif if ($#Missing_CVS) then set Incomplete echo "${procedure}: Missing package CVS subdirectories -" foreach package ($Missing_CVS) echo " $package" end echo endif if ($?Incomplete) then echo "$procedure aborted." exit 2 endif # Tag each package. set CWD = $PWD set version = $Tag-$version echo $version @ total = 0 foreach package ($Packages) echo "==> $package" cd $Directory/$package if ($?Verbose) then cvs -q tag $version set exit_status = $status echo else set tagged = `cvs -q tag $version` set exit_status = $status endif if ($exit_status) then echo '\!\!\!' "There was a problem tagging the $package package." echo " cvs exit status $exit_status" cd $CWD exit $exit_status endif if (! $?Verbose) then @ count = $#tagged / 2 @ total += $count echo " $count files tagged" endif cd $CWD end if (! $?Verbose) then echo " $total total files tagged" endif exit 0 pirl-2.3.8/PIRL/Build/LICENSE_GPL_30000644000175000017500000007733011742730035016116 0ustar mathieumathieu 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 pirl-2.3.8/PIRL/Build/LICENSE_LGPL_30000644000175000017500000001674311742730035016233 0ustar mathieumathieu GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. pirl-2.3.8/PIRL/Build/README0000644000175000017500000002352111742731525015043 0ustar mathieumathieuThe PIRL Java Packages PIRL packages: Conductor - An application to manage procedure pipelines. Includes a Reference_Resolver for Database and PVL Parameter reference resolution. The Maestro sub-package includes applications for remote real-time monitoring and management of Conductors. Messenger - A set of classes for managing synchronous and asynchronous network message passing between processes. A class that multiplexes a styled text Writer via Messengers is included. Database - A simplified interface for access to, and basic management of, relational SQL databases. PVL - The PIRL Parameter Value Language package provides for the input, output and manipulation of parameters and their data values. A PVL_to_DB class is provided to map PVL Parameter Values into Database fields for update or insert operations. Configuration - Management of configuration files using PVL syntax. Configuration objects are typically used by application programs. Strings - Character string manipulation capabilities used by the other PIRL packages. Viewers - Classes for common capabilities associated with GUI viewers, plus GUI viewers associated with other PIRL packages. Image_Tools - Classes for obtaining metadata information from JP2 files, plus an ImageInputStream implementation for access to files remotely via an HTTP server. Utilities - Support classes for abstract data stream connection, system host identification, public-private key authentication, styled text writing, numerical ranges, logical to canonical pathname mapping, and unit test checking. The package distribution files are available from the PIRL distribution site (ftp://pirlftp.lpl.arizona.edu/pub/Java/). All of the packages are contained in a compiled classes jar file (PIRL.jar) or a source code tarball (PIRL.tar.gz). Files with -N.N.N version extensions, where N.N.N is the distribution version, are also available; the files without the extension are links to the files with the most current version. A Makefile (using gmake syntax) is included in the source code distribution that can be used to compile the class files. The tarball unpacks into a subdirectory named PIRL-N.N.N, where N.N.N is the distribution version. A link to this directory named PIRL is required to satisfy Java's dependency on filenames being identical to package/class names. See the Source code build section, below. The Apps subdirectory of the distribution directory contains wrapper scripts for the applications of the PIRL Java Packages. The wrappers, when installed in users' command PATH, can make executing the applications, including locating runtime dependencies, as easy as if the applications were a self-contained executables. The Configs subdirectory of the distribution directory contains configuration file templates typically used by various applications. Note: The distribution files containing the PIRL Java Packages do not include the third party dependency packages (below) which are necessary to run various applications. These packages are redistributed on the PIRL Java Packages distribution site. Documentation: The documentation is available online from the http://PIRL.LPL.Arizona.edu/software/PIRL_Java_Packages/ URL. The javadoc is distributed in the PIRL-javadoc.tar.gz tarball file. The source code distribution file (PIRL.tar.gz) contains the javadoc HTML files in the docs subdirectory. Third party packages: Java Components For Mathematics - A project at Hobart and William Smith Colleges, Java Components for Mathematics (http://math.hws.edu/javamath/) is an effort to develop a framework of configurable mathematical software components written in the Java programming language. A tarball of the complete version 1.0 distribution is provided (jcm.tar.gz). However, this is a large tarball and only the edu.hws.jcm.data package is used by the PIRL software, so a jar file for just this package (jcm_data.jar) is provided. JDBC drivers for MySQL - Implementations of the JDBC API for the MySQL (www.mysql.com) and PostgreSQL (http://www.postgresql.org) relational database servers. The MySQL Connector/J package is deemed to be the official driver by MySQL and is definitely to be preferred for its performance, features and maintenance support. It is provided in both source code tarball (mysql-connector.tar.gz) and compiled classes jar (mysql-connector.jar) forms. The PostgreSQL JDBC Driver (http://jdbc.postgresql.org) is provided in both source code tarball (postgresql-jdbc.tar.gz) and compiled classes jar (postgresql-jdbc.jar) forms. SwingX - The SwingLabs SwingX project (http://swinglabs.org) provides GUI components that extend various JFC Swing package classes. The JXTable class is used by Conductor applications. SwingX is provided in distribution kit (swingx.tar.gz) and compiled classes jar (swingx.jar) forms. Commons-CLI - The Apache Commons CLI package (http://commons.apache.org/cli/) provides command line interface parsing. This package is used by the Pipeline_Configuration subpackage of the Conductor package. Commons-CLI is provided in both source code zip (commons-cli.zip) and compiled classes jar (commons-cli.jar) forms. JavaMail and JavaBeans Activation Framework - The JavaX JavaMail package (http://www.oracle.com/technetwork/java/javamail/) provides email message sending functionality. This package is used by the Notify utility of the Conductor pacakge which also requires the JavaBeans Activation Framework (http://java.sun.com/products/archive/javabeans/jaf11.html) at runtime. JavaMail is provided in distribution package (javamail.zip) and compiled classes jar (javamail.jar) forms; JavaBeans Activation Framework is provided in a distribution package (jaf.zip). Note: These packages are only required when Notify must use an "unfriendly" email server. They may not be required at all, in which case a Notify-simple.java implementation is provided that only uses JFC classes and worked fine for many years until a "sophisticated" email server was encountered. Simply replace Notify.java with Notify-simple.java before compiling the Conductor pacakge to get the simple version. N.B.: The pre-built PIRL.jar distribution requires the additional packages. System requirements: A Java 1.5, or greater, version of the Java Virtual Machine (JVM) is required. The PIRL Java Packages software should function correctly with just the appropriate JVM and the jars files from the distribution - PIRL.jar, jcm_data.jar, mysql-connector.jar, postgresql-jdbc.jar and swingx.jar - installed in the Java extensions directory or located in the users' CLASSPATH. As of v2.2.0 of the PIRL Java Packages the Conductor application no longer requires a patch to the JFC Process class implementation. The Native_Methods.c file in the Conductor package is used to build a libNative_Methods dynamic library (see the Source code build section) that provides the Unix process ID of the JVM which is used to uniquely identify a Conductor instantiation on a host system. While not necessary to run Conductor it is recommended. Source code build: The source code distribution requires the javac compiler from the Java Software Development Kit (SDK) to build the class files from the java files. The third party jars, or their class files, are required dependencies to build the PIRL Java Packages. Check the definitions of the dependency variables at the beginning of each Makefile to make site-specific adjustments for their locations. The javadoc utility is required to build the documentation files. The GNU make (gmake) utility is required to use the Makefiles included in each package directory. The Build subdirectory contains a Makefile that will build all the packages (gmake all) and the javadoc HTML docs files (gmake docs), and each package directory contains a Makefile that will build just the package. The GNU C compiler (gcc) is used by the Conductor/Makefile to build the libNative_Methods dynamic library. The JNI support in the SDK will also be needed to build the library. The library that is built will be located in a Conductor subdirectory named . where is the name of the operating system (e.g. Darwin, Linux or SunOS) and is the name of the machine architecture (e.g. i386, powerpc, x86_64 or sparc). The library must be installed at a location that can be found at Conductor runtime by the JVM. Typical system locations for the library are /usr/lib or /usr/local/lib, or it can be located in any path listed in the LD_LIBRARY_PATH (DYLD_LIBRARY_PATH for Apple OS X) environment variable. if not in a standard system location, the LD_LIBRARY_PATH (DYLD_LIBRARY_PATH for Mac OS X systems) environment variable can be set to include the pathname to the installation location. Copyright: The PIRL Java Packages are Copyright (C) 2001 - 2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. They are distributed under the terms of the GNU Lesser General Public License as published by the Free Software Foundation. A copy of this license should be included with the distributed files. If not, see . The PIRL Java Packages are is distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. Contact: Comments, questions and contributions of code or corrections regarding the PIRL Java Packages may be sent to the author: Bradford Castalia Castalia@Arizona.edu Principal Systems Analyst 520-621-2197 Planetary Image Research Laboratory Department of Planetary Sciences University of Arizona Space Sciences Bldg. 1541 E. University Blvd. Tucson, Arizona 85721-0063 1.9 2012/04/16 05:51:17 pirl-2.3.8/PIRL/Build/PIRL_icon.gif0000644000175000017500000000423010526757522016430 0ustar mathieumathieuGIF89a[0f3f333ff33fff333f̙3fff33ffff̙f333̙f3ff3ff333ff3333̙3f3fff̙f33333fffff̙f3̙3fff3333ff3fff33f̙!M,[0:M:66ƽ˰ȲA̼!ֳͿѭ!!زIa#LJ"oܾ }78( bc1B Be@Ac!aHq(tĜސ² p.!Bp@ EĔX4Ŭ PĈ+,Xa?.Lr! Ŏ!CvlAO:t@]t_'0qD,<%lP!UY$$x .-ҌF$@"xڑV6q`MݢUh_0B8:݂SBfOߛ. Tl@vTH?!&zֵW.7|D5"?XƄlYc{$ p"Zq阢k!`9.jҎ@B, @Zh`d ( * 9N?|-yc,ͧ < *ZFAGҸH 9kr0L=5V̐0v*T5*jYf_$0N raİ,- ʑ F @`66QB r3. ł<\|} l$N61BJ vv Di%+*E[ju0ʦF譳Xz;!*F%TB0 #󦼏J,-~vdVkAFJh֥\d ,S`d.l* %1f&;r Ȥ q$PG7 ']UF 7Gm3QC= I0J xOFPDJ Ao l 2)B ;@&( A`B), .y k&:ꪊ50ĵ_yB8W8MAO= 9" ?!07:,j!IC!e.` +x 4 Z0@ @0APXJvF0 2t2П(?'?(!0$p3cOA &k˟vAMk" 7؏atA(Lp:%2B v2+X@swʘ90GDRYr"Ǝ$ IB0 vQ-2?Iq4-6g;[~pBˑCoc>U"K ,R#U)"}qL< $Q X¦rpރxC&`#q1Р-{s4`$d%g1`q@Cn$?[~ (UUSE }g\0Ո)ǫ(&Y6P=r PF,lB0% XDJ=(B8I # DS3 Ie> Nb$J@Vm`F 3@1JmEEBEPL0;pirl-2.3.8/PIRL/Build/PIRL-MANIFEST.template0000644000175000017500000000034610531225150017655 0ustar mathieumathieuName: PIRL Specification-Title: PIRL Java Packages Specification-Version: VERSION Specification-Vendor: University of Arizona Implementation-Title: PIRL Implementation-Version: VERSION Implementation-Vendor: University of Arizona pirl-2.3.8/PIRL/Build/Copyright.html0000644000175000017500000000357211742731557017032 0ustar mathieumathieu Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona.

The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Comments, corrections, contributions, and queries can be sent to the author at the address below. To be put on an email list for update notifications send your request to the email address below along with a brief description of how you use the software.

Bradford Castalia Castalia@Arizona.edu
Senior Systems Analyst  
University of Arizona Tucson, Arizona
Department of Planetary Sciences Planetary Image Research Laboratory

"Build an image in your mind, fit yourself into it."
pirl-2.3.8/PIRL/Build/Makefile0000644000175000017500000002132611743640171015621 0ustar mathieumathieu# Makefile for the PIRL Java packages. # # gmake syntax. # # PIRL CVS ID: Makefile,v 2.13 2012/04/18 22:30:17 castalia Exp TITLE := PIRL Java Packages R := R CVS_ID := [Makefile 2.13] # Installation location for the application wrappers. INSTALL_DIR ?= /opt/local/sh # The order of packages matters: put packages after their dependencies. PACKAGES := Utilities \ Strings \ TreeTable \ PVL \ Configuration \ Messenger \ Viewers \ Database \ Conductor \ Image_Tools # Subpackages to be included in the javadoc. SUBPACKAGES := PIRL.Conductor.Maestro \ PIRL.Conductor.Pipeline_Configuration # Distribution version. VERSION := $(shell grep '^[0-9]' VERSION) ifeq ($(strip $(VERSION)),) VERSION := 0.0 endif # Third party jars: # The location of the MySQL JDBC driver. MySQL_JDBC_DIR ?= /opt/java/mysql-connector MySQL_JDBC_JAR ?= mysql-connector.jar MySQL := $(MySQL_JDBC_DIR)/$(MySQL_JDBC_JAR) # The location of the PostgreSQL JDBC driver. PostgreSQL_JDBC_DIR ?= /opt/java/PostgreSQL PostgreSQL_JDBC_JAR ?= postgresql.jar PostgreSQL := $(PostgreSQL_JDBC_DIR)/$(PostgreSQL_JDBC_JAR) # The location of the Java Components for Mathematics. JCM_DIR ?= /opt/java/jcm JCM_DATA_JAR ?= jcm_data.jar JCM := $(JCM_DIR)/$(JCM_DATA_JAR) # The location of the SwingLabs Swing Component Extensions. SwingX_DIR ?= /opt/java/SwingX SwingX_JAR ?= swingx.jar SwingX := $(SwingX_DIR)/$(SwingX_JAR) # The location of the Apache Commons Comand Line Interface package. Commons_CLI_DIR ?= /opt/java/commons-cli Commons_CLI_JAR ?= commons-cli.jar Commons_CLI := $(Commons_CLI_DIR)/$(Commons_CLI_JAR) # The location of the JavaX JavaMail package. JavaMail_DIR ?= /opt/java/javamail JavaMail_JAR ?= mail.jar JavaMail := $(JavaMail_DIR)/$(JavaMail_JAR) # Java CLASSPATH for the packages and their dependencies. JPATH ?= ../../:$(MySQL):$(PostgreSQL):$(JCM):$(SwingX):$(Commons_CLI):$(JavaMail) # The location where the generated PIRL javadocs will be installed. PIRL_DOCS_DIR ?= ../docs # The location of the Sun JFC documentation (package-list file). # Change this URL to your local installation if preferred. JAVA_DOCS_DIR ?= http://PIRL.LPL.Arizona.edu/resources/guide/software/Java/api #JAVA_DOCS_DIR ?= /opt/java/docs/api # Distributions: # The location for distribution files. DIST_DIR ?= /data/ftp/pub/Java # Versioned directory to contain released files. PIRL_VERSION := PIRL-$(VERSION) # PIRL tarball. PIRL_TARBALL := $(PIRL_VERSION).tar.gz # PIRL jar. PIRL_JAR := $(PIRL_VERSION).jar # PIRL javadocs. PIRL_JAVADOC := PIRL-javadoc-$(VERSION).tar.gz # Application wrappers and their config files. APPLICATIONS := Configuration/Configuration \ Conductor/Conductor \ Conductor/Create_Pipeline \ Conductor/Notify \ Conductor/Pipeline_Source \ Conductor/Pipeline_Source_Manager \ Conductor/Evaluate \ Conductor/Maestro/Kapellmeister \ Conductor/Maestro/Stage_Manager \ Conductor/Maestro/Manage_Stage_Manager \ Conductor/Pipeline_Configuration/Pipeline_Configuration \ Database/Data_View \ Database/Query_DB \ Database/Update_DB \ PVL/PVL_to_DB \ Image_Tools/JP2_Info \ Image_Tools/JPEG2000_Codestream_Info CONFIGURATIONS := Conductor/Conductor.conf \ Conductor/Maestro/Kapellmeister.conf \ Conductor/Maestro/Stage_Manager.conf \ Database/Database.conf # Fully qualified package names. PACKAGE_PATHS := $(PACKAGES:%=PIRL.%) # Distribution boilerplate files. BOILERPLATE := README \ Copyright_Notice.GPL \ LICENSE_GPL_3 \ LICENSE_LGPL_3 # Targets: all: .BEGIN packages .END packages: $(PACKAGES) $(PACKAGES): @echo @echo "--> $@" $(MAKE) -C ../$@ $(TARGET) # Documentation docs: @echo @echo "--- Building the documentation" javadoc \ -d $(PIRL_DOCS_DIR) \ -author \ -version \ -link $(JAVA_DOCS_DIR) \ -windowtitle "PIRL Java Packages" \ -header 'PIRL' \ -bottom 'Copyright (C) \ 2003-2009 Bradford Castalia, University of Arizona' \ -sourcepath ../.. \ -classpath $(JPATH) \ $(PACKAGE_PATHS) $(SUBPACKAGES) cp PIRL_icon.gif Copyright.html $(PIRL_DOCS_DIR) # Application wrappers installation: install: @echo @echo "=== Installing application wrappers in $(INSTALL_DIR)" cp -f $(addprefix ../,$(APPLICATIONS)) $(INSTALL_DIR) # Distribution files: # Source code tarball tarball: $(PIRL_TARBALL) $(PIRL_TARBALL): @echo @echo "=== $@ ===" @echo -rm -f $@ @if [ ! -d staging_area/PIRL ]; then \ echo ; \ echo "--- Assembling the package files" ; \ mkdir -p staging_area/PIRL ; \ Release -dir staging_area/PIRL -no_make Build $(PACKAGES) ; \ fi @echo mv staging_area/PIRL staging_area/$(PIRL_VERSION) cd staging_area; gtar czf ../$@ $(PIRL_VERSION) mv staging_area/$(PIRL_VERSION) staging_area/PIRL # Javadoc tarball javadoc.tarball: $(PIRL_JAVADOC) $(PIRL_JAVADOC): @echo @echo "=== $@ ===" @echo -rm -f $@ @if [ ! -d staging_area/PIRL/docs ]; then \ echo ; \ echo "--- Assembling the package files" ; \ mkdir -p staging_area/PIRL ; \ Release -dir staging_area/PIRL -no_make Build $(PACKAGES) ; \ fi @echo $(MAKE) -C staging_area/PIRL/Build docs mv staging_area/PIRL staging_area/$(PIRL_VERSION) cd staging_area; gtar czf ../$@ $(PIRL_VERSION)/docs mv staging_area/$(PIRL_VERSION) staging_area/PIRL # Jar jar: $(PIRL_JAR) $(PIRL_JAR): @echo @echo "=== $@ ===" @echo -rm -f $@ @echo "--- Preparing the jar files" @if [ ! -d staging_area/PIRL/Build ]; then \ echo ; \ echo "--- Assembling the package files" ; \ mkdir -p staging_area/PIRL ; \ Release -dir staging_area/PIRL -no_make Build $(PACKAGES) ; \ fi @echo $(MAKE) -C staging_area/PIRL/Build @echo cd staging_area/PIRL/Build; cp -f $(BOILERPLATE) VERSION .. @echo @echo "--- Removing extraneous files" mv staging_area/PIRL/Build staging_area rm -rf staging_area/PIRL/docs find staging_area/PIRL/Conductor \ -type d -name '*.*' \ -exec rm -rf {} \; -prune find staging_area/PIRL \ -type d -name tests \ -exec rm -rf {} \; -prune find staging_area/PIRL \ \( -name '*.java' -o -name Makefile -o -name '*.html' \) \ -exec rm {} \; @echo @echo "--- Preparing the MANIFEST" sed -e s/VERSION/$(VERSION)/ \ -e '/MAIN/ d' \ PIRL-MANIFEST.template > MANIFEST @echo @echo "--- Filling the jar" cd staging_area; jar cmf ../MANIFEST ../$(PIRL_JAR) PIRL -rm -f MANIFEST dist: .BEGIN tarball javadoc.tarball jar .END distribute: dist @echo @echo "=== Distribution ===" @echo @echo "--- Archiving the tarballs and jar files to the archive directory." mkdir -p ../archive cp -f $(PIRL_TARBALL) $(PIRL_JAVADOC) $(PIRL_JAR) ../archive @echo @echo "--- Moving the distribution files to the $(DIST_DIR) directory." mkdir -p $(DIST_DIR) mv -f \ $(PIRL_TARBALL) \ $(PIRL_JAVADOC) \ $(PIRL_JAR) \ $(DIST_DIR) rm -f \ $(DIST_DIR)/$(PIRL_TARBALL:-$(VERSION).tar.gz=.tar.gz) \ $(DIST_DIR)/$(PIRL_JAVADOC:-$(VERSION).tar.gz=.tar.gz) \ $(DIST_DIR)/$(PIRL_JAR:-$(VERSION).jar=.jar) ln -s $(PIRL_TARBALL) $(DIST_DIR)/$(PIRL_TARBALL:-$(VERSION).tar.gz=.tar.gz) ln -s $(PIRL_JAVADOC) $(DIST_DIR)/$(PIRL_JAVADOC:-$(VERSION).tar.gz=.tar.gz) ln -s $(PIRL_JAR) $(DIST_DIR)/$(PIRL_JAR:-$(VERSION).jar=.jar) cd staging_area/Build; cp -f $(BOILERPLATE) $(DIST_DIR) @echo @echo "--- Copying the application files to the $(DIST_DIR)/Apps directory." rm -rf $(DIST_DIR)/Apps mkdir -p $(DIST_DIR)/Apps cp -f $(addprefix staging_area/PIRL/,$(APPLICATIONS)) $(DIST_DIR)/Apps @echo @echo "--- Copying the configuration files to the $(DIST_DIR)/Configs directory." rm -rf $(DIST_DIR)/Configs mkdir -p $(DIST_DIR)/Configs cp -f $(addprefix staging_area/PIRL/,$(CONFIGURATIONS)) $(DIST_DIR)/Configs @echo @echo "--- Cleanup" rm -rf staging_area rm -rf staging_area rm -rf staging_area # Cleaning: clean: TARGET = clean clean: packages distclean: dist_clean dist_clean: rm -f $(PIRL_TARBALL) $(PIRL_TARBALL:-$(VERSION).tar.gz=.tar.gz) rm -f $(PIRL_JAVADOC) $(PIRL_JAVADOC:-$(VERSION).tar.gz=.tar.gz) rm -f $(PIRL_JAR) $(PIRL_JAR:-$(VERSION).jar=.jar) rm -rf MANIFEST staging_area # Release: release: @echo @echo "*** $(TITLE) release $(VERSION)" @echo Release -d .. -no_make -no_strip $(PACKAGES) @echo $(MAKE) packages docs @echo @echo "*** $(TITLE) release $(VERSION) completed." # Reporting: .BEGIN: @echo @echo "*** $(TITLE) $(CVS_ID) ***" @date @echo .END: @echo @echo "*** $(TITLE) Completed ***" @date @echo .PHONY: .BEGIN \ all packages $(PACKAGES) clean docs \ tarball javadoc.tarball jar \ dist distribute distclean dist_clean wipe release \ .END pirl-2.3.8/PIRL/Build/VERSION0000644000175000017500000000014612052275051015221 0ustar mathieumathieuPIRL Java Packages release version: 2.3.8 CVS ID: VERSION,v 1.107 2012/11/19 00:15:37 castalia Exp pirl-2.3.8/PIRL/Image_Tools/0000755000175000017500000000000012052546526015323 5ustar mathieumathieupirl-2.3.8/PIRL/Image_Tools/JPEG2000_Info.java0000644000175000017500000011056611742733714020203 0ustar mathieumathieu/* JPEG2000_Info HiRISE CVS ID: JPEG2000_Info.java,v 1.25 2012/04/16 06:10:20 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.EOFException; import java.lang.ArithmeticException; import java.net.URL; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.net.ProtocolException; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.FileImageInputStream; import PIRL.PVL.*; import PIRL.Strings.String_Buffer; /** JPEG2000_Info is a Parameter Aggregate describing a file of JPEG2000 information.

This class is intended to be the base class for JP2_Info and JPEG2000_Codestream_Info Parameter sets. The JPEG2000_Info methods know how to manage the assigned ImageInputStream to obtain data values and keep track of the stream position, but it expects its subclasses to know how to use the data values to generate the appropriate Parameters.

@author Bradford Castalia UA/PIRL @version 1.25 @see PIRL.Image_Tools.JP2_Info @see PIRL.Image_Tools.JPEG2000_Codestream_Info */ public class JPEG2000_Info extends Parameter { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Image_Tools.JPEG2000_Info (1.25 2012/04/16 06:10:20)"; /** Parameter name for specifying the position of data in the Source file (byte offset from the beginning of the file). */ public static final String DATA_POSITION_PARAMETER = "^Data_Position"; /** Parameter name for specifying a data block offset in an enclosing object.

For JP2 boxes, the offset is relative to the byte immediately following the box header's length, type and extended length (if present) fields.

For codestream segements, the offset is relative to the byte immediately following the segment marker and length fields. */ public static final String DATA_OFFSET_PARAMETER = "Data_Offset"; /** Parameter name for specifying the length (bytes) of a data block. */ public static final String DATA_LENGTH_PARAMETER = "Data_Length"; /** Parameter name for specifying the bit precision of a value. */ public static final String VALUE_BITS_PARAMETER = "Value_Bits"; /** Value of a bit precision Parameter value when the number of bits is variable. */ public static final int BITS_VARIABLE = 0; /** Generic parameter names used by both JP2_Info and JPEG20000_Codestream_Info */ public static final String LENGTH_PARAMETER = "Length", POSITION_PARAMETER = "^Position", TOTAL_COMPONENTS_PARAMETER = "Total_Components"; //! Warning message parameter name. public static final String WARNING_PARAMETER = "WARNING"; //! Name for an unrecognized box type or segment marker. public static final String UNKNOWN = "Unknown"; /** The ImageInputStream Source from which the JPEG2000 information is obtained.

@see #Source() */ protected ImageInputStream Image_Input_Stream = null; /** The current read position of the Source file.

@see #Stream_Position() */ protected long Stream_Position = -1; /** Default flag for skipping tile segments.

@see #Skip_Tiles(boolean) */ public static boolean Skip_Tiles_Default = true; /** Flag for skipping tile segments.

@see #Skip_Tiles(boolean) */ protected boolean Skip_Tiles = Skip_Tiles_Default; /** Default flag for using {@link #Data_Position_Parameters(Parameter, long) data position parameters} in preference to {@link #Data_Offset_Parameters(Parameter, long, long) data offset parameters}.

@see #Use_Data_Position(boolean) */ public static boolean Use_Data_Position_Default = true; /** Flag for using {@link #Data_Position_Parameters(Parameter, long) data position parameters} in preference to {@link #Data_Offset_Parameters(Parameter, long, long) data offset parameters}.

@see #Use_Data_Position(boolean) */ protected boolean Use_Data_Position = Use_Data_Position_Default; /** Default flag for throwing an IllegalStateException when the Source is found to contain an invalid box or segment organization or contents. @param source The source from which to obtain the JPEG2000_Info. This may be a local filename or a remote URL reference. @throws UnknownHostException If the source is a URL but the hostname is specifies can not be resolved. @throws IllegalArgumentException If the source is a URL but does not specify the http protocol or a source pathname. @throws IOException If there was a problem reading the file. */ public JPEG2000_Info ( String source ) throws IOException, UnknownHostException, IllegalArgumentException { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Info: " + source); Classification (AGGREGATE); Source (source); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Info"); } /** Construct a JPEG2000_Info for a File.

@param file The File from which to obtain the JPEG2000_Info. @throws IOException If there was a problem reading the file. */ public JPEG2000_Info ( File file ) throws IOException { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Info: " + file); Classification (AGGREGATE); Source (file); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Info"); } /** Construct a JPEG2000_Info for an ImageInputStream.

@param image_input_stream The ImageInputStream from which to obtain the JPEG2000_Info. @throws IOException If there was a problem reading the file. */ public JPEG2000_Info ( ImageInputStream image_input_stream ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Info: " + image_input_stream); Classification (AGGREGATE); Source (image_input_stream); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Info"); } /** Construct a an empty JPEG2000_Info Object.

Use one of the Source methods to assign a source file. */ public JPEG2000_Info () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< JPEG2000_Info"); Classification (AGGREGATE); } /*============================================================================== Accessors */ /** Assign a named file as the information source.

An attempt is made to form a URL from the source. If this succeeds an HTTP_ImageInput_Stream is constructed from the URL and then delegated to the {@link #Source(ImageInputStream)} method.

If a URL can not be formed from the source a File is constructed from the source and then delegated to the {@link #Source(File)} method.

@param source The name of the source to use as the information source. @return This JPEG2000_Info. @throws UnknownHostException If the source is a URL but the hostname is specifies can not be resolved. @throws IllegalArgumentException If the source is a URL but does not specify the http protocol or a source pathname. @throws FileNotFoundException If the file could not be accessed for any reason. @throws ProtocolException If the source is a valid URL but an HTTP {@link HTTP_ImageInputStream#Status() Status} protocol error was returned by the server. @throws IOException If there was a problem reading the source. */ public JPEG2000_Info Source ( String source ) throws IOException, UnknownHostException, IllegalArgumentException { if (source == null) return this; try { URL url = new URL (source); HTTP_ImageInputStream image_input_stream = null; try {image_input_stream = new HTTP_ImageInputStream (url);} catch (IllegalArgumentException exception) { throw new IllegalArgumentException (ID + '\n' + "Inaccessble URL: " + url + '\n' + exception.getMessage ()); } catch (UnknownHostException exception) { throw new UnknownHostException (ID + '\n' + "Inaccessble URL: " + url + '\n' + exception.getMessage ()); } catch (FileNotFoundException exception) { throw new FileNotFoundException (ID + '\n' + "Inaccessble URL: " + url + '\n' + exception.getMessage ()); } catch (ProtocolException exception) { throw new ProtocolException (ID + '\n' + "Inaccessble URL: " + url + '\n' + exception.getMessage ()); } catch (IOException exception) { throw new IOException (ID + '\n' + "Inaccessble URL: " + url + '\n' + exception.getMessage ()); } if (DEBUG != DEBUG_OFF) image_input_stream.Logging (true); Name (image_input_stream.Source_URL ().toString ()); return Source (image_input_stream); } catch (MalformedURLException exception) {} return Source (new File (source)); } /** Assign a File as the information source.

The name of this JPEG2000_Info is set to the canonical pathname of the File.

An ImageInputStream is constructed from the file and then delegated to the {@link #Source(ImageInputStream)} method.

@param file The File to use as the information source. @return This JPEG2000_Info. @throws FileNotFoundException If the file could not be accessed for any reason. @throws IOException If there was a problem reading the file. */ public JPEG2000_Info Source ( File file ) throws IOException { if (file == null) return this; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> JPEG2000_Info.Source: File " + file.getCanonicalPath ()); if (! file.exists ()) throw new FileNotFoundException (ID + '\n' +"Unable to obtain JP2 Info for Source file " + file.getCanonicalPath () + '\n' +" because no file exists at that pathname."); if (! file.isFile ()) throw new FileNotFoundException (ID + '\n' +"Unable to obtain JP2 Info for Source file " + file.getCanonicalPath () + '\n' +" because the file is not a regular file."); if (! file.canRead ()) throw IO_Error ("Unable to obtain JP2 Info for Source file " + file.getCanonicalPath () + '\n' +" because the file can not be read."); ImageInputStream image_input_stream = null; try {image_input_stream = new FileImageInputStream (file);} catch (IOException exception) { throw IO_Error ("Unable to obtain JP2 Info for Source file " + file.getCanonicalPath () + '\n' +" because an ImageInputStream could not be created for it.", exception); } Name (file.getCanonicalPath ()); Source (image_input_stream); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JPEG2000_Info.Source: File " + file.getCanonicalPath ()); return this; } /** Assign an ImageInputStream as the information source.

If the image_input_stream is null or identical to the currently assigned ImageInputStream nothing is done.

If this JPEG2000_Info does not have a name it is set to "ImageInputStream". All Parameters are removed. The current {@link #Stream_Position() stream position} is determined.

@param image_input_stream The ImageInputStream to use as the information source. @return This JPEG2000_Info. */ public JPEG2000_Info Source ( ImageInputStream image_input_stream ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> JPEG2000_Info.Source: " + image_input_stream + '\n' +" Current Image_Input_Stream: " + Image_Input_Stream); if (image_input_stream != null && image_input_stream != Image_Input_Stream) { Image_Input_Stream = image_input_stream; try {Stream_Position = Image_Input_Stream.getStreamPosition ();} catch (IOException exception) { /* Shouldn't happen. Just assume the beginning of the stream. */ Stream_Position = 0; } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Image_Input_Stream reset to " + Image_Input_Stream); if (Name ().length () == 0) Name ("ImageInputStream"); // Remove any and all current parameters. Remove_All (); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JPEG2000_Info.Source"); return this; } /** Get the currently assigned ImageInputStream information source.

@return The currently assigned ImageInputStream. This will be null if no source has been assigned. */ public ImageInputStream Source () {return Image_Input_Stream;} /** Enable of disable skipping of tile segments.

When a codestream is being scanned for segment markers the tile-part segments - which begin with a Start-Of_Tile (SOT) marker - may be skipped. When there are many tiles in the codestream this will significantly reduce the amount of time to scan the Source and the number of Parameter groups generated.

@param enabled If true, tile-part segments will be skipped; otherwise all tile-part segments will be examined and Parameter groups assembled for each one. @return This JPEG2000_Info. */ public JPEG2000_Info Skip_Tiles ( boolean enabled ) {Skip_Tiles = enabled; return this;} /** Test if tile segments will be skipped.

@return true if tile segments will be skipped; false otherwise. @see #Skip_Tiles(boolean) */ public boolean Skip_Tiles () {return Skip_Tiles;} /** Enable or disable the use of Source data position parameters.

@param enabled If true, Source data position parameters are included for all JP2 boxes and codestream segments, and used elsewhere. Otherwise position parameters are not included and data offset parameters are used elsewhere instead of position parameters. @return This JPEG2000_Info. */ public JPEG2000_Info Use_Data_Position ( boolean enabled ) {Use_Data_Position = enabled; return this;} /** Test if Source data position parameters will be used.

@return true if Source data position parameters will be used; false otherwise. @see #Use_Data_Position(boolean) */ public boolean Use_Data_Position () {return Use_Data_Position;} /** Enable or disable throwing of IllegalStateException when an invalid JP2 box or codestream segment structure or contents is encountered.

@param enabled If true an IllegalStateException will be thrown at the point where the JP2 box or codestream segment structure is determined to be invalid. @return This JPEG2000_Info. */ public JPEG2000_Info Throw_Illegal_State ( boolean enabled ) {Throw_Illegal_State = enabled; return this;} /** Test if IllegalStateException will be thrown when an invalid JP2 box or codestream segment structure or contents is encountered.

@return true if IllegalStateException will be thrown when an invalid JP2 box or codestream segment structure or contents is encountered; false otherwise. @see #Throw_Illegal_State(boolean) */ public boolean Throw_Illegal_State () {return Throw_Illegal_State;} /*============================================================================== Manipulators */ /** Get the next long data value from the Source.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return The next long data value from the Source. @throws IOException If there was a problem reading from the Source. */ protected long Read_long ( String description ) throws IOException { try { long datum = Image_Input_Stream.readLong (); Stream_Position += 8; return datum; } catch (IOException exception) {throw IO_Error ("Unable to read " + description + " value.", exception);} } /** Get the next int data value from the Source.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return The next int data value from the Source. @throws IOException If there was a problem reading from the Source. */ protected int Read_int ( String description ) throws IOException { try { int datum = Image_Input_Stream.readInt (); Stream_Position += 4; return datum; } catch (IOException exception) {throw IO_Error ("Unable to read " + description + " value.", exception);} } /** Get the next int data value from the Source as an unsigned integer.

The next {@link #Read_int(String) int value read from the Source} is cast as a long value but without sign extension.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return A long value of the next int from the Source as an unsigned integer value. @throws IOException If there was a problem reading from the Source. */ protected long Read_Unsigned_int ( String description ) throws IOException {return (long)Read_int (description) & 0xFFFFFFFF;} /** Get the next short data value from the Source.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return An int value of the next short from the Source as a signed integer value. @throws IOException If there was a problem reading from the Source. */ protected int Read_short ( String description ) throws IOException { try { int datum = Image_Input_Stream.readShort (); Stream_Position += 2; return datum; } catch (IOException exception) {throw IO_Error ("Unable to read " + description + " value.", exception);} } /** Get the next short data value from the Source as an unsigned integer.

The next {@link #Read_short(String) short value read from the Source} is cast as an int value but without sign extension.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return An int value of the next short from the Source as an unsigned integer value. @throws IOException If there was a problem reading from the Source. */ protected int Read_Unsigned_short ( String description ) throws IOException {return (int)Read_short (description) & 0xFFFF;} /** Get the next byte data value from the Source.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return The next byte data value, as an int with sign extension from the byte, from the Source. @throws IOException If there was a problem reading from the Source. */ protected int Read_byte ( String description ) throws IOException { try { int datum = Image_Input_Stream.readByte (); ++Stream_Position; return datum; } catch (IOException exception) {throw IO_Error ("Unable to read " + description + " value.", exception);} } /** Get the next byte data value from the Source as an unsigned integer.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @return An int value of the next byte from the Source as an unsigned integer value. @throws IOException If there was a problem reading from the Source. */ protected int Read_Unsigned_byte ( String description ) throws IOException {return Read_byte (description) & 0xFF;} /** Get a value of some bits precision from the Source.

If the number of bits is negative the most significant bit of the value is treated as a sign bit (extended in the returned value) and the actual precision of the value is abs (bits). The Source stream is read starting at the current {@link #Stream_Position() stream position}. Bytes are read from the stream starting with the most significant byte of the value with the most significant bits right justified in this first byte; the least significant bit is presumed to be the least significant bit of the last byte read. The bytes read are concatenated to form the value that is returned.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @param bits The precision of the value is abs (bits). Negative bits means the value is signed; i.e. the most significant bit is the sign bit. @return The value obtained as a long. Thus a maximum of 64 bits may be obtained. As a special case, if bits is 0, 0 is returned. @throws IOException If there was a problem reading the value bytes from the Source stream. @throws ArithmeticException If abs (bits) > 64. */ protected long Read_Value ( String description, int bits ) throws IOException { long value = 0; if (bits != 0) { boolean signed_value = false; if (bits < 0) { bits = -bits; if (bits < 64 && // 64-bit value does not need sign extension. bits > 1) // 1-bit value is always unsigned. signed_value = true; } if (bits > 64) throw new ArithmeticException (ID + '\n' + "Unable to Read Value of " + bits + " bits."); // Convert bits to bytes. int bytes = bits >> 3; if ((bits % 8) != 0) bytes++; // Move the data bytes into the local variable. while (bytes != 0) { value <<= 8; value |= Read_Unsigned_byte (description + " byte " + bytes); --bytes; } if (signed_value && (value & (1 << (bits - 1))) != 0) { // Sign extension. long mask = 0; while (bits != 0) { mask <<= 1; mask |= 1; --bits; } value |= ~mask; } } return value; } /** Get a String from the Source.

Starting at the current {@link #Stream_Position() stream position} bytes are read up to, but not including, the end position. Each byte read is concatenated as a character in the resultant String with unprintable characters being expanded into printable escape sequences. However, if the last character is null it is not included in the String.

The current {@link #Stream_Position() stream position} is updated.

@param description A String describing the value being obtained. @param end_position The end position in the Source after the last byte to be read. @return A String representing the bytes read from the source. @throws IOException If there was a problem reading from the Source. */ protected String Read_String ( String description, long end_position ) throws IOException { String describe = description + " string"; char character; String_Buffer string = new String_Buffer (); while (Stream_Position < end_position) { if ((character = (char)Read_Unsigned_byte (describe)) == 0) break; if (Stream_Position < end_position || character != '\0') string.append (character); } Stream_Position (end_position); // Escape any non-printable characters. string.special_to_escape (); return string.toString (); } /** Set the stream position of the information Source.

If the specified position is the same as the current position of the Source, nothing is done. Otherwise the current position of the Source is changed to the specified position.

@param position The position, as a byte offset from the beginning of the file, to which to reposition the image Source stream. This value must be a non-negative value less than the size of the Source file, otherwise an IOException will be thrown. @throws IOException If the Source stream could not be repositioned. An attempt will be made to reset the Source stream to return it to the last marked position before the exception is thrown. */ protected void Stream_Position ( long position ) throws IOException { if (Stream_Position != position) { Exception exception = null; try { Image_Input_Stream.seek (position); Stream_Position = position; } catch (IndexOutOfBoundsException except) {exception = except;} catch (IOException except) {exception = except;} if (exception != null) { String message = "Could not reposition the Source to position " + position + '.'; try {Image_Input_Stream.reset ();} catch (IOException e) { message += "\n" + exception.getMessage () + '\n' + "Could not reset the previous position."; exception = e; } throw IO_Error (message, exception); } } } /** Get the current source stream position.

@return The current source stream position as a byte offset from the beginning of the file. */ public long Stream_Position () {return Stream_Position;} /** Generate an IOException containing a message.

If the provided message does not already contain the class {@link #ID} the ID and a NL character is prepended to the message (or the ID becomes the message if it is null). If a non-null exception is provided, the message from the exception is appended, after a NL character, to the message used in constructing the IOException.

If the exception argument is an instance of EOFException the returned IOException will also be an EOFException. If the exception argument is non-null and contains a cause, an attempt is made to apply the cause to the new IOException (failure to apply the cause is ignored).

@param message The message String to be included in the IOException. @return An IOException. @see #IOException(String, Exception) */ private IOException IO_Error ( String message, Exception exception ) { if (message == null) message = ID; else if (message.indexOf (ID) < 0) message = ID + '\n' + message; if (exception != null) message += "\n" + exception.getMessage (); IOException except; if (exception instanceof EOFException) except = new EOFException (message); else except = new IOException (message); if (exception != null) { Throwable cause = exception.getCause (); if (cause != null) { try {except.initCause (cause);} catch (IllegalStateException e) {} } } return except; } /** Generate an IOException containing a message.

@param message The message String to be included in the IOException. @return An IOException. @see #IOException(String, Exception) */ private IOException IO_Error ( String message ) {return IO_Error (message, null);} /*============================================================================== Parameters */ /** Add data position Parameters to a Parameter Group.

A {@link #DATA_POSITION_PARAMETER} is added to the group with the current {@link #Stream_Position() stream position} as its value. A {@link #DATA_LENGTH_PARAMETER} is also added to the group with the number of bytes between the current stream position (inclusive) and the end position (exclusive) as its value.

N.B.: If {@link #Use_Data_Position(boolean) using data position parameters} is disabled {@link #Data_Offset_Parameters(Parameter, long, long) data offset parameters} will be used instead for the same values.

@param group The Parameter Group (Aggregate) to receive the data location parameters. @param end_position The end position in the Source of the data as a byte offset from the beginning of the file to the byte immediately following the last byte of the data segement. If this value is not greater than zero no {@link #DATA_LENGTH_PARAMETER} is included. @throws IllegalArgumentException If the current {@link #Stream_Position() stream position} is greater than the data end position. @see #Data_Offset_Parameters(Parameter, long, long) */ protected void Data_Position_Parameters ( Parameter group, long end_position ) throws IllegalArgumentException { if (end_position > 0 && Stream_Position > end_position) throw new IllegalArgumentException (ID + '\n' + "Invalid " + group.Name () + " data position parameter values -\n" + " start = " + Stream_Position + '\n' + " end = " + end_position); if (Use_Data_Position) { try { group .Add (new Parameter (DATA_POSITION_PARAMETER) .Value (new Value (Stream_Position) .Units ("byte offset"))); if (end_position > 0) group .Add (new Parameter (DATA_LENGTH_PARAMETER) .Value (new Value (end_position - Stream_Position) .Units ("bytes"))); } catch (PVL_Exception exception) {} } else Data_Offset_Parameters (group, Stream_Position, end_position); } /** Add data offset Parameters to a Parameter Group.

A {@link #DATA_OFFSET_PARAMETER} is added to the group followed by a a {@link #DATA_LENGTH_PARAMETER}.

@param group The Parameter Group (Aggregate) to receive the data offset parameters. @param start_offset The offset (in bytes relative to 0) of the data location - excluding segment marker and length elements - in the enclosing object. @param end_offset The offset of the end (byte immediately following the last data byte) of the data block. If this value is not greater than zero no {@link #DATA_LENGTH_PARAMETER} is included. @throws IllegalArgumentException If the data start offser is greater than the end offset. #Stream_Position() stream position} is greater than the data end position. @see #Data_Position_Parameters(Parameter, long) */ protected void Data_Offset_Parameters ( Parameter group, long start_offset, long end_offset ) throws IllegalArgumentException { if (end_offset > 0 && start_offset > end_offset) throw new IllegalArgumentException (ID + '\n' + "Invalild " + group.Name () + " data offset parameter values -\n" + " start = " + start_offset + '\n' + " end = " + end_offset); try { group .Add (new Parameter (DATA_OFFSET_PARAMETER) .Value (new Value (start_offset).Units ("byte offset"))); if (end_offset > 0) group .Add (new Parameter (DATA_LENGTH_PARAMETER) .Value (new Value (end_offset - start_offset).Units ("bytes"))); } catch (PVL_Exception exception) {} } /** Generate a {@link #VALUE_BITS_PARAMETER} from the source.

Signed byte values are read from the Source starting at the current {@link #Stream_Position() stream position} up to, but not including, the end position. The value is incremented by 1. If the byte value read is negative only the least significant 7 bits are valid; the resultant value is the negative of the value of these bits. A negative value signals that the values whose bit precision is the absolute of the value is a signed value. A value of zero signals that the actual precision values are indictated elsewhere.

Each value read is added to an Array Value that is assigned to a Parameter named {@link #VALUE_BITS_PARAMETER} which is what is returned.

@param description A String describing the values being obtained. @param end_position The end position in the Source of the values as a byte offset from the beginning of the file to the byte immediately following the last byte to be read. @return An Assignment Parameter named {@link #VALUE_BITS_PARAMETER} that has an Array Value with the adjust values read from the Source. @throws IOException If there was a problem reading from the Source. */ protected Parameter Value_Bits_Parameter ( String description, long end_position ) throws IOException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JPEG2000_Info.Value_Bits_Parameter: " + description + '\n' +" From stream position " + Stream_Position + '\n' +" Until stream position " + end_position); String describe = description + ' ' + VALUE_BITS_PARAMETER; Parameter parameter = null; try { Value list = new Value ().Type (Value.SEQUENCE); int datum; while (Stream_Position < end_position) { datum = Read_byte (describe) + 1; if (datum < 0) datum = -(datum & 0x7F); list.Add (new Value (datum)); } parameter = new Parameter (VALUE_BITS_PARAMETER) .Comments ("\nNegative bits indicate signed values of abs (bits);\n" +" Zero bits indicate variable number of bits.") .Value (list); } catch (PVL_Exception exception) {} if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JPEG2000_Info.Value_Bits_Parameter: " + parameter.Description ()); return parameter; } /** Generate a {@link #VALUE_BITS_PARAMETER} from an Array of raw Values.

The datum of each Value in the array is assumed to be a raw bit precision value. This value is incremented by 1. If the value is negative the least significant 7 bits are taken and then negated to signal that the image values are signed. The image value precision of each component is the absolute value of the corresponding entry in the array.

The resultant Array is assigned to a Parameter named {@link #VALUE_BITS_PARAMETER} which is what is returned.

@param array A Array Value containing raw bit precision values. @return An Assignment Parameter named {@link #VALUE_BITS_PARAMETER} that has a Value that is an Array of the adjusted array values. */ protected static Parameter Value_Bits_Parameter ( Value array ) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JPEG2000_Info.Value_Bits_Parameter:\n" +" From Value " + array.Description ()); Parameter parameter = null; try { Value value; long datum; for (int index = 0; index < array.getChildCount (); index++) { value = array.Get (index); datum = value.long_Data () + 1; if (datum < 0) datum = -(datum & 0x7F); value.Data (datum); } parameter = new Parameter (VALUE_BITS_PARAMETER) .Comments ("\nNegative bits indicate signed values of abs (bits);\n" +" Zero bits indicate variable number of bits.") .Value (array); } catch (PVL_Exception exception) {} if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JPEG2000_Info.Value_Bits_Parameter: " + parameter.Description ()); return parameter; } /** Get the Array Value of a Parameter as an int array.

@param parameter_name The name of the parameter to use. The first Parameter found with this name, which may be a pathname, is used. @return An int array. @see #int_Array(Parameter) */ public int[] int_Array ( String parameter_name ) {return int_Array (Find (parameter_name));} /** Get the Array Value of a Parameter as an int array.

@param parameter The Parameter from which to obtain Array Values. @return An int array. @see #int_Array(Value) */ public static int[] int_Array ( Parameter parameter ) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JP2_Info.int_Array (Parameter): " + ((parameter == null) ? "null" : parameter.Path_Name ())); int[] values = new int[0]; if (parameter != null && parameter.Is_Assignment ()) { try {values = int_Array (parameter.Value ());} catch (PVL_Exception exception) {} } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JP2_Info.int_Array (Parameter)"); return values; } /** Convert an Array Value to an int array.

Each Integer Value in the Array Value is used to set the respective value of an int array. Any Value in the Array that is not an Integer is ignored; Sub-Array Values are not entered.

@param value The Array Value to convert. @return An int array. N.B.: The number of elements in the array may be less than the number of Values in the Parameter's Array if it contains non-Integer Values. If the Parameter is not an Assignment or its Value is not an Array an empty array will be returned. */ public static int[] int_Array ( Value value ) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JP2_Info.int_Array (Value)"); int[] values = null; try { Value list; if (value != null && value.Is_Array ()) { int index, count = 0, total_entries = value.getChildCount (); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" total_entries = " + total_entries); values = new int[total_entries]; Value entry; for (index = 0; index < total_entries; index++) { entry = value.Get (index); if (entry.Is_Integer ()) values[count++] = (int)entry.long_Data (); } if (count < index) { // Contract the int array. int[] int_array = new int[count]; for (index = 0; index < count; index++) int_array[index] = values[index]; values = int_array; } } else values = new int[0]; } catch (PVL_Exception exception) {} if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JP2_Info.int_Array: " + values); return values; } } pirl-2.3.8/PIRL/Image_Tools/Polar_Stereographic_Spherical_Projection.java0000644000175000017500000001411211742733714026311 0ustar mathieumathieu/* Polar_Stereographic_Spherical_Projection PIRL CVS ID: Polar_Stereographic_Spherical_Projection.java,v 1.3 2012/04/16 06:10:20 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import java.awt.Point; import java.awt.geom.Point2D; public class Polar_Stereographic_Spherical_Projection extends Projection { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Polar_Stereographic_Spherical_Projection (1.3 2012/04/16 06:10:20)"; private static final String PROJECTION_NAME = "Polar Stereographic Spherical"; private double Center_Latitude_Sign, Center_Latitude_Sine, Center_Latitude_Cosine, Polar_Radius_times_2; /*============================================================================== Constructors */ public Polar_Stereographic_Spherical_Projection ( Parameter parameters ) throws PVL_Exception { // Initialize the base projection parameters. super (parameters); if (Not_Identity) { // Precalculated coefficients. if (Center_Latitude < 0.0) Center_Latitude_Sign = -1.0; else Center_Latitude_Sign = 1.0; Center_Latitude_Sine = StrictMath.sin (Center_Latitude); Center_Latitude_Cosine = StrictMath.cos (Center_Latitude); Polar_Radius_times_2 = Polar_Radius * 2.0; } } /*============================================================================== Accessors */ public String Name () {return PROJECTION_NAME;} /*============================================================================== Converters */ /** Get the world longitude,latitude coordinate for an image sample,line coordinate.

The conversion algorithm is:

@param image_coordinate The image sample,line coordinate. @return The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees within the range 0-360. */ public Point2D.Double to_World ( Point2D image_coordinate ) throws IllegalArgumentException, ArithmeticException { if (image_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project image coordinates (" + (int)image_coordinate.getX () + ',' + (int)image_coordinate.getX () + ") to world coordinates\n" + "because the image coordinates point is null."); Point2D.Double world_coordinate = null; if (Not_Identity) { double x = Sample_to_Projection_X (image_coordinate.getX ()), y = Line_to_Projection_Y (image_coordinate.getY ()), P = StrictMath.sqrt ((x * x) + (y * y)), C = 2.0 * StrictMath.atan (P / Polar_Radius_times_2); double latitude = StrictMath.asin ( (StrictMath.cos (C) * Center_Latitude_Sine) + (y * StrictMath.sin (C) * Center_Latitude_Cosine / P)); if (Math.abs (latitude) > PI_OVER_2) throw new ArithmeticException (ID + '\n' + "Projection of image coordinates (" + (int)image_coordinate.getX () + ',' + (int)image_coordinate.getX () + ") to world coordinates\n" + "resulted in an invalid latitude of " + Math.toDegrees (latitude) + " degrees."); if (Planetocentric) latitude = Planetographic_to_Planetocentric (latitude); latitude = StrictMath.toDegrees (latitude); double longitude = Center_Longitude + StrictMath.atan (x / -Center_Latitude_Sign * y); if (Positive_West) longitude *= -1.0; longitude = To_360 (StrictMath.toDegrees (longitude)); world_coordinate = new Point2D.Double (longitude, latitude); } else world_coordinate = new Point2D.Double (image_coordinate.getX (), image_coordinate.getY ()); return world_coordinate; } /** Get the image sample,line coordinate for a world longitude,latitude coordinate.

The conversion algorithm is:

@param world_coordinate The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees. @return The image sample,line coordinate. */ public Point to_Image ( Point2D world_coordinate ) throws IllegalArgumentException { if (world_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project world coordinates to image coordinates\n" + "because the world coordinates point is null."); Point image_coordinate = null; if (Not_Identity) { double longitude = StrictMath.toRadians (world_coordinate.getX ()), latitude = StrictMath.toRadians (world_coordinate.getY ()); if (Planetocentric) latitude = Planetographic_to_Planetocentric (latitude); if (Positive_West) longitude *= -1.0; double rho = Polar_Radius_times_2 * StrictMath.tan (PI_OVER_4 - (Center_Latitude_Sign * 0.5 * latitude)), distance = longitude - Center_Longitude; image_coordinate = new Point ( (int)(Projection_X_to_Sample (rho * StrictMath.sin (distance)) + 0.5), // Round to nearest pixel. (int)(Projection_Y_to_Line (rho * StrictMath.cos (distance) * -Center_Latitude_Sign) + 0.5) // Round to nearest pixel. ); } else image_coordinate = new Point ((int)image_coordinate.getX (), (int)image_coordinate.getY ()); return image_coordinate; } } pirl-2.3.8/PIRL/Image_Tools/Makefile0000644000175000017500000000112010662176534016760 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.3 2007/08/20 02:33:00 castalia Exp # gmake syntax JPATH ?= ../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Multibuf_ImageInputStream.class \ HTTP_ImageInputStream.class \ JPEG2000_Info.class \ JPEG2000_Codestream_Info.class \ JP2_Info.class \ Projection.class \ Equirectangular_Projection.class \ Polar_Stereographic_Elliptical_Projection.class \ Polar_Stereographic_Spherical_Projection.class all: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Image_Tools/JPEG2000_Codestream_Info.java0000644000175000017500000022436712052546364022354 0ustar mathieumathieu/* JPEG2000_Codestream_Info HiRISE CVS ID: JPEG2000_Codestream_Info.java,v 1.32 2012/11/20 00:21:08 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import java.io.File; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.EOFException; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import java.nio.ByteOrder; import java.util.Hashtable; import PIRL.PVL.*; // Main dependencies. import PIRL.Viewers.Parameter_View; import java.util.Vector; import java.util.Iterator; /** JPEG2000_Codestream_Info provides a set of PVL Parameters that describe the contents of a JPEG2000 codestream.

The JPEG2000 codestream format is defined in Part 1 of the JPEG2000 International Standard (ISO/IEC 15444-1:2004(E); "Information technology - JPEG 2000 image coding system: Core coding system"). This format is composed entirely of a contiguous sequence of codestream segment. Each segment is prefaced with a marker that identifies the segement. The marker is followed by length. Boxes may be nested within other boxes.

@author Bradford Castalia UA/PIRL @version 1.32 @see PIRL.Image_Tools.JP2_Info @see PIRL.Image_Tools.JPEG2000_Info */ public class JPEG2000_Codestream_Info extends JPEG2000_Info { public static final String ID = "PIRL.Image_Tools.JPEG2000_Codestream_Info (1.32 2012/11/20 00:21:08)"; //! The name of this Parameter Aggregate. public static final String CODESTREAM = "Codestream"; //! Marker codes. public static final int RESERVED_DELIMITER_MARKER_MIN = 0xFF30, RESERVED_DELIMITER_MARKER_MAX = 0xFF3F, SOC_MARKER = 0xFF4F, SOT_MARKER = 0xFF90, SOD_MARKER = 0xFF93, EOC_MARKER = 0xFFD9, SIZ_MARKER = 0xFF51, COD_MARKER = 0xFF52, COC_MARKER = 0xFF53, RGN_MARKER = 0xFF5E, QCD_MARKER = 0xFF5C, QCC_MARKER = 0xFF5D, POC_MARKER = 0xFF5F, TLM_MARKER = 0xFF55, PLM_MARKER = 0xFF57, PLT_MARKER = 0xFF58, PPM_MARKER = 0xFF60, PPT_MARKER = 0xFF61, SOP_MARKER = 0xFF91, EPH_MARKER = 0xFF92, CRG_MARKER = 0xFF63, COM_MARKER = 0xFF64; //! Marker names. public static final String // Delimiting markers and marker segments. SOC_NAME = "Start_of_Codestream", SOT_NAME = "Start_of_Tile_Part", SOD_NAME = "Start_of_Data", EOC_NAME = "End_of_Codestream", // Fixed information marker segments. SIZ_NAME = "Size", // Functional marker segments. COD_NAME = "Coding_Style_Default", COC_NAME = "Coding_Style_Component", RGN_NAME = "Region_of_Interest", QCD_NAME = "Quantization_Default", QCC_NAME = "Quantization_Component", POC_NAME = "Progression_Order_Change", // Pointer marker segments. TLM_NAME = "Tile_Lengths", PLM_NAME = "Packet_Length_Main", PLT_NAME = "Packet_Length_Tile", PPM_NAME = "Packed_Packet_Main", PPT_NAME = "Packed_Packet_Tile", // In-bit-stream markers and marker segments. SOP_NAME = "Start_of_Packet", EPH_NAME = "End_of_Packet_Header", // Information marker segments. CRG_NAME = "Component_Registration", COM_NAME = "Comment"; private static Hashtable Marker_Names = new Hashtable (); static { Marker_Names.put (new Integer (SOC_MARKER), SOC_NAME); Marker_Names.put (new Integer (SOT_MARKER), SOT_NAME); Marker_Names.put (new Integer (SOD_MARKER), SOD_NAME); Marker_Names.put (new Integer (EOC_MARKER), EOC_NAME); Marker_Names.put (new Integer (SIZ_MARKER), SIZ_NAME); Marker_Names.put (new Integer (COD_MARKER), COD_NAME); Marker_Names.put (new Integer (COC_MARKER), COC_NAME); Marker_Names.put (new Integer (RGN_MARKER), RGN_NAME); Marker_Names.put (new Integer (QCD_MARKER), QCD_NAME); Marker_Names.put (new Integer (QCC_MARKER), QCC_NAME); Marker_Names.put (new Integer (POC_MARKER), POC_NAME); Marker_Names.put (new Integer (TLM_MARKER), TLM_NAME); Marker_Names.put (new Integer (PLM_MARKER), PLM_NAME); Marker_Names.put (new Integer (PLT_MARKER), PLT_NAME); Marker_Names.put (new Integer (PPM_MARKER), PPM_NAME); Marker_Names.put (new Integer (PPT_MARKER), PPT_NAME); Marker_Names.put (new Integer (SOP_MARKER), SOP_NAME); Marker_Names.put (new Integer (EPH_MARKER), EPH_NAME); Marker_Names.put (new Integer (CRG_MARKER), CRG_NAME); Marker_Names.put (new Integer (COM_MARKER), COM_NAME); } //! Marker code parameter. public static final String MARKER_PARAMETER = "Marker"; //! SOT parameters. public static final String TILE_INDEX_PARAMETER = "Tile_Index", TILE_PART_LENGTH_PARAMETER = "Tile_Part_Length", TILE_PART_INDEX_PARAMETER = "Tile_Part_Index", TOTAL_TILE_PARTS_PARAMETER = "Total_Tile_Parts"; //! SIZ parameters. public static final String CAPABILITY_PARAMETER = "Capability", REFERENCE_GRID_WIDTH_PARAMETER = "Reference_Grid_Width", REFERENCE_GRID_HEIGHT_PARAMETER = "Reference_Grid_Height", HORIZONTAL_IMAGE_OFFSET_PARAMETER = "Horizontal_Image_Offset", VERTICAL_IMAGE_OFFSET_PARAMETER = "Vertical_Image_Offset", TILE_WIDTH_PARAMETER = "Tile_Width", TILE_HEIGHT_PARAMETER = "Tile_Height", HORIZONTAL_TILE_OFFSET_PARAMETER = "Horizontal_Tile_Offset", VERTICAL_TILE_OFFSET_PARAMETER = "Vertical_Tile_Offset", HORIZONTAL_SAMPLE_SPACING_PARAMETER = "Horizontal_Sample_Spacing", VERTICAL_SAMPLE_SPACING_PARAMETER = "Vertical_Sample_Spacing"; //! COD parameters. public static final String CODING_STYLE_PARAMETER = "Coding_Style", PROGRESSION_ORDER_PARAMETER = "Progression_Order", TOTAL_QUALITY_LAYERS_PARAMETER = "Total_Quality_Layers", MULTIPLE_COMPONENT_TRANSFORM_PARAMETER = "Multiple_Component_Transform", TOTAL_RESOLUTION_LEVELS_PARAMETER = "Total_Resolution_Levels", CODE_BLOCK_WIDTH_PARAMETER = "Code_Block_Width", CODE_BLOCK_HEIGHT_PARAMETER = "Code_Block_Height", CODE_BLOCK_STYLE_PARAMETER = "Code_Block_Style", TRANSFORM_PARAMETER = "Transform", PRECINCT_SIZE_PARAMETER = "Precinct_Size"; //! Coding style bit flag masks. public static final int ENTROPY_CODER_FLAG = 1 << 0, SOP_FLAG = 1 << 1, EPH_FLAG = 1 << 2; //! Progression order values. public static final int LRCP_PROGRESSION_ORDER = 0, RLCP_PROGRESSION_ORDER = 1, RPCL_PROGRESSION_ORDER = 2, PCRL_PROGRESSION_ORDER = 3, CPRL_PROGRESSION_ORDER = 4; public static final String PROGRESSION_ORDERS[] = { "Layer-Resolution-Component-Position", "Resolution-Layer-Component-Position", "Resolution-Position-Component-Layer", "Position-Component-Resolution-Layer", "Component-Position-Resolution-Layer" }; //! Code block style bit flag masks. public static final int SELECTIVE_ARITHMETIC_BYPASS_FLAG = 1 << 0, RESET_CONTEXT_PROBABILITIES = 1 << 1, TERMINATION_FLAG = 1 << 2, VERTICALLY_CAUSAL_CONTEXT_FLAG = 1 << 3, PREDICTABLE_TERMINATION_FLAG = 1 << 4, SEGMENTATION_SYMBOLS_FLAG = 1 << 5; //! Transform values. public static final int TRANSFORM_IRREVERSIBLE = 0, TRANSFORM_REVERSIBLE = 1; //! COC parameters. public static final String COMPONENT_INDEX_PARAMETER = "Component_Index"; //! RGN parameters. public static final String ROI_STYLE_PARAMETER = "ROI_Style", IMPLICIT_SHIFT_PARAMETER = "Implicit_Shift"; //! QCD parameters. public static final String QUANTIZATION_STYLE_PARAMETER = "Quantization_Style", TOTAL_GUARD_BITS_PARAMETER = "Total_Guard_Bits", STEP_SIZE_PARAMETER = "Step_Size"; //! Quantization style bit field masks. public static final int NO_QUANTIZATION = 0, QUANTIZATION_SCALAR_DERIVED = 1, QUANTIZATION_SCALAR_EXPOUNDED = 2; //! POC parameters. public static final String LEVEL_INDEX_PARAMETER = "Level_Index", LAYER_INDEX_PARAMETER = "Layer_Index"; //! TLM parameters. public static final String INDEX_PARAMETER = "Index", TILE_INDEX_SIZE_PARAMETER = "Tile_Index_Size", TILE_PART_LENGTH_SIZE_PARAMETER = "Tile_Part_Length_Size"; //! PLM and PLT parameters. public static final String PACKET_LENGTH_PARAMETER = "Packet_Length", CONTINUATION_PARAMETER = "Continuation"; // PLM and PLT continuation state variables. private Parameter PLM_Packet_Length_Parameter = null, PLT_Packet_Length_Parameter = null; private long PLM_Packet_Length = 0, PLT_Packet_Length = 0, PLM_Packet_Length_Bytes_Remaining = 0, PLT_Packet_Length_Bytes_Remaining = 0; //! CRG parameters. public static final String HORIZONTAL_COMPONENT_OFFSET_PARAMETER = "Horizontal_Component_Offset", VERTICAL_COMPONENT_OFFSET_PARAMETER = "Vertical_Component_Offset"; //! COM parameters. public static final String DATA_TYPE_PARAMETER = "Data_Type", BINARY_DATA_PARAMETER = "Binary_Data", TEXT_DATA_PARAMETER = "Text_Data"; //! Data type values. public static final int DATA_TYPE_BINARY = 0, DATA_TYPE_TEXT = 1; //------------------------------------------------------------------------------ //! Codestream validity flags. private int Codestream_Validity = 0; /** {@link #Codestream_Validity() Codestream validity} bit mask for the SOC segment found flag. */ public static final int SOC_FOUND = 1 << 0; /** {@link #Codestream_Validity() Codestream validity} bit mask for the SIZ segment found flag. */ public static final int SIZ_FOUND = 1 << 1; /** {@link #Codestream_Validity() Codestream validity} bit mask for the COD segment found flag. */ public static final int COD_FOUND = 1 << 2; /** {@link #Codestream_Validity() Codestream validity} bit mask for the QCD segment found flag. */ public static final int QCD_FOUND = 1 << 3; /** {@link #Codestream_Validity() Codestream validity} bit mask for the EOC segment found flag. */ public static final int EOC_FOUND = 1 << 4; /** {@link #Codestream_Validity() Codestream validity} value indicating that a complete codestream with all required segments were found. N.B.: The final EOC delimiter is not included as a required segment as it is usually not searched for. */ public static final int CODESTREAM_COMPLETE = SOC_FOUND | SIZ_FOUND | COD_FOUND | QCD_FOUND; /** Flag for missing EOC search through the codestream.

If true and the End of Codestream ({@link #EOC_MARKER EOC}) is not found because the last tile-part segment has a zero length value, the remaining codestream data will be searched for the EOC marker. If false this search - which can be very time cosuming for a very large codestream segment - will not be done and the {@link #Codestream_Validity() codestream validity value} will not have the {@link #EOC_FOUND} flag set.

By default the EOC search is disabled. */ public static boolean EOC_Search = false; /** Flag whether a zero length tile-part was found in the codestream. */ private boolean Zero_Length_Tile_Part = false; //! Image geometry information. private long Reference_Grid_Width = -1, Reference_Grid_Height = -1, Image_Width = -1, Image_Height = -1, Horizontal_Image_Offset = -1, Vertical_Image_Offset = -1, Tile_Width = -1, Tile_Height = -1, Horizontal_Tile_Offset = -1, Vertical_Tile_Offset = -1; private int[] Pixel_Height = new int[0], Pixel_Width = new int[0]; //! Commonly accessed codestream info: private int Total_Components = -1, Total_Tiles = -1, Total_Quality_Layers = -1, Progression_Order = -1, Total_Resolution_Levels = -1, Transform = -1; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_PARAMETERS = 1 << 2, DEBUG_HELPERS = 1 << 3, DEBUG_MAIN = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a JPEG2000_Codestream_Info for a named file.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param filename The filename from which to obtain the JPEG2000_Codestream_Info. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( String filename ) throws IOException {this (filename, Skip_Tiles_Default);} /** Construct a JPEG2000_Codestream_Info for a named file.

@param filename The filename from which to obtain the JPEG2000_Codestream_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( String filename, boolean skip_tiles ) throws IOException { super (filename); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Codestream_Info: " + filename); Read (0, Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info"); } /** Construct a JPEG2000_Codestream_Info for a File.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param file The File from which to obtain the JPEG2000_Codestream_Info. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( File file ) throws IOException {this (file, Skip_Tiles_Default);} /** Construct a JPEG2000_Codestream_Info for a File.

@param file The File from which to obtain the JPEG2000_Codestream_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( File file, boolean skip_tiles ) throws IOException { super (file); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Codestream_Info: " + file); Read (0, Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info"); } /** Construct a JPEG2000_Codestream_Info for an ImageInputStream.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param image_input_stream The ImageInputStream from which to obtain the JPEG2000_Codestream_Info. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( ImageInputStream image_input_stream ) throws IOException {this (image_input_stream, Skip_Tiles_Default);} /** Construct a JPEG2000_Codestream_Info for an ImageInputStream.

@param image_input_stream The ImageInputStream from which to obtain the JPEG2000_Codestream_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If there was a problem reading the file. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( ImageInputStream image_input_stream, boolean skip_tiles ) throws IOException { super (image_input_stream); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Codestream_Info: " + image_input_stream); Read (0, Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info"); } /** Construct a JPEG2000_Codestream_Info for an ImageInputStream and Reads the stream up to an end position.

@param image_input_stream The ImageInputStream containing a JPEG2000 codestream. The stream is presumed to be positioned at the beginning of the codestream. @param end_position The byte position (exclusive) in the stream where the codestream ends. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If a problem is encountered while reading the stream. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info ( ImageInputStream image_input_stream, long end_position, boolean skip_tiles ) throws IOException { super (image_input_stream); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JPEG2000_Codestream_Info: " + image_input_stream + '\n' +" end_position " + end_position); Read (end_position, Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info"); } /** Construct a an empty JPEG2000_Codestream_Info Object.

Use one of the Source methods to assign a source file. Then use a Read method to obtain the information parameters.

@see JPEG2000_Info#Source(String) @see #Read(long, boolean) */ public JPEG2000_Codestream_Info () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< JPEG2000_Codestream_Info"); } /*============================================================================== Accessors */ /** Read JPEG2000 codestream information from the Source file.

If no Source file has been assigned, nothing is done.

The Source is read from its current position up to, but not including, the end position. If the end position is 0 the file is read until an End_of_Codestream segment is found, no more recognized segments are found, or the end of file. If an unrecognized marker is encountered the stream is repositioned back to the location where the marker was read.

Any information Parameters currently present are removed. Then the file is scanned for JPEG2000 codestream marker segments. Each segment found is used to define the contents of a Parameter Group added to the base JPEG2000_Info Aggregate. The name of each Parameter Group is the name associated with the marker. The Group always contains the following parameters:

Marker
The marker code in numeric hexadecimal form.
^Position
The position of the segment in the Source as a byte offset (from 0). N.B.: This parameter is not present if {@link JPEG2000_Info#Use_Data_Position(boolean) data position inclusion} is disabled.
Length
The length of the segment in bytes.

The remaining Parameters in each Group depend on the segment type.

When JP2 information boxes are nested, the corresponding Parameter Groups are also nested. JPEG2000 codestream (Contiguous_Codestream, jp2c) boxes contain a Codestream Parameter Group that is defined by a JPEG2000_Info object.

N.B.: Unless {@link JPEG2000_Info#Throw_Illegal_State(boolean) illegal state detection} is disabled, a {@link #WARNING_PARAMETER} will be placed at the end of the Parameter set and further Source file scanning halted if the previous segment is invalid. The Value of the warning Parameter will be a String describing the problem. Possible sources of warning parameters are: The beginning of the codestream does not contain a valid Start_of_Codestream segment or the second segment is not a valid Size segment.

@param end_position The location in the Source stream before which reading is to stop. If 0 reading stops at the first unrecognized segment marker or the end of file. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @return This JPEG2000_Codestream_Info. @throws IOException If there was a problem reading the Source file. @see JPEG2000_Info */ public JPEG2000_Codestream_Info Read ( long end_position, boolean skip_tiles ) throws IOException { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> JPEG2000_Codestream_Info.Read"); if (Image_Input_Stream == null) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info.Read: Image_Input_Stream is null"); return this; } ByteOrder byte_order = Image_Input_Stream.getByteOrder (); if (byte_order != ByteOrder.BIG_ENDIAN) Image_Input_Stream.setByteOrder (ByteOrder.BIG_ENDIAN); // Remove any and all current parameters. Remove_All (); // Reset all codestream characterization values. Reset_Codestream_Values (); try { Stream_Position = Image_Input_Stream.getStreamPosition (); Add_Segments (this, end_position, skip_tiles); } catch (IOException exception) { throw IO_Error ("Unable to obtain JPEG2000 Codestream Info from Source" + " at position " + Stream_Position, exception); } catch (IllegalStateException exception) { try {Add (new Parameter (WARNING_PARAMETER) .Value (exception.getMessage ()));} catch (PVL_Exception except) {} } finally { Image_Input_Stream.setByteOrder (byte_order); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info.Read"); return this; } /** Read JPEG2000 codestream information from the Source file.

@return This JPEG2000_Codestream_Info. @throws IOException If there was a problem reading the Source. @see #Read(long, boolean) */ public JPEG2000_Codestream_Info Read () throws IOException {return Read (0, Skip_Tiles);} /** Test if the Source file contains a complete codestream.

The Source must be {@link #Read(long, boolean) read} for this test to be valid.

@return true if the {@link #Codestream_Validity() Codestream Validity} value has all the {@link #CODESTREAM_COMPLETE} flags set; false otherwise. */ public boolean Is_Complete_Codestream () {return (Codestream_Validity & CODESTREAM_COMPLETE) == CODESTREAM_COMPLETE;} /** Get the codestream validity flags.

@return An int value holding the codestream validity flags. This will be 0 if the value is not yet known. @see #SIZ_FOUND @see #COD_FOUND @see #QCD_FOUND @see #EOC_FOUND @see #CODESTREAM_COMPLETE */ public int Codestream_Validity () {return Codestream_Validity;} /** Get the total number of components identified in the codestream.

This value corresponds to the value of the {@link #TOTAL_COMPONENTS_PARAMETER}.

@return The total number of components in the codestream. This will be -1 if the value is not yet known. */ public int Total_Components () {return Total_Components;} /** Get the width of the image.

The image width .

@return The image width in reference grid units. This will be -1 if the value is not yet known. */ public long Image_Width () {return Image_Width;} /** Get the height of the image.

This value corresponds to the value of the {@link #REFERENCE_GRID_HEIGHT_PARAMETER}.

@return The image height in reference grid units. This will be -1 if the value is not yet known. */ public long Image_Height () {return Image_Height;} /** Get the width of the image components reference grid.

This value corresponds to the value of the {@link #REFERENCE_GRID_WIDTH_PARAMETER}.

@return The image components reference grid width. This will be -1 if the value is not yet known. */ public long Reference_Grid_Width () {return Reference_Grid_Width;} /** Get the height of the image components reference grid.

This value corresponds to the value of the {@link #REFERENCE_GRID_HEIGHT_PARAMETER}.

@return The image components reference grid height. This will be -1 if the value is not yet known. */ public long Reference_Grid_Height () {return Reference_Grid_Height;} /** Get the horizontal offset of the image on the reference grid.

The horizontal offset to the upper left corner of the image area will always be positive (to the right relative to the reference grid origin at the upper left corner of the reference grid). This value corresponds to the value of the {@link #HORIZONTAL_IMAGE_OFFSET_PARAMETER}.

@return The horizontal offset of the image on the reference grid. This will be -1 if the value is not yet known. */ public long Horizontal_Image_Offset () {return Horizontal_Image_Offset;} /** Get the vertical offset of the image on the reference grid.

The vertical offset to the upper left corner of the image area will always be positive (downwards relative to the reference grid origin at the upper left corner of the reference grid). This value corresponds to the value of the {@link #VERTICAL_IMAGE_OFFSET_PARAMETER}.

@return The horizontal offset of the image on the reference grid. This will be -1 if the value is not yet known. */ public long Vertical_Image_Offset () {return Vertical_Image_Offset;} /** Get the width of a component's pixel.

The width of a pixel is measured in reference grid units. Each component of the image may have a differenct pixel width. This value array corresponds to the array of the {@link #HORIZONTAL_SAMPLE_SPACING_PARAMETER}.

@return An int array of pixel width values in component order. This will be an empty array if the values are not yet known. */ public int[] Pixel_Width () {return Pixel_Width;} /** Get the height of a component's pixel.

The height of a pixel is measured in reference grid units. Each component of the image may have a differenct pixel height. This value array corresponds to the array of the {@link #VERTICAL_SAMPLE_SPACING_PARAMETER}.

@return An int array of pixel height values in component order. This will be an empty array if the values are not yet known. */ public int[] Pixel_Height () {return Pixel_Height;} /** Get the tile width.

This value corresponds to the value of the {@link #TILE_WIDTH_PARAMETER}.

@return The tile width measured in reference grid units. This will be -1 if the value is not yet known. */ public long Tile_Width () {return Tile_Width;} /** Get the tile height.

This value corresponds to the value of the {@link #TILE_HEIGHT_PARAMETER}.

@return The tile height measured in reference grid units. This will be -1 if the value is not yet known. */ public long Tile_Height () {return Tile_Height;} /** Get the horizontal offset of the tiles on the reference grid.

The horizontal offset to the upper left corner of the upper left tile will always be positive (to the right relative to the reference grid origin at the upper left corner of the reference grid) and no greather than the {@link #Horizontal_Image_Offset() horizontal image offset}. This value corresponds to the value of the {@link #HORIZONTAL_IMAGE_OFFSET_PARAMETER}.

@return The horizontal offset of the tiles on the reference grid. This will be -1 if the value is not yet known. */ public long Horizontal_Tile_Offset () {return Horizontal_Tile_Offset;} /** Get the vertical offset of the tiles on the reference grid.

The vertical offset to the upper left corner of the upper left tile will always be positive (downwards relative to the reference grid origin at the upper left corner of the reference grid) and no greather than the {@link #Vertical_Image_Offset() vertical image offset}. This value corresponds to the value of the {@link #VERTICAL_IMAGE_OFFSET_PARAMETER}.

@return The horizontal offset of the tiles on the reference grid. This will be -1 if the value is not yet known. */ public long Vertical_Tile_Offset () {return Vertical_Tile_Offset;} /** Get the total tiles expected to be found in the codestream.

@return The total number of tiles expected, but not necessarily found, in the codestream. This will be -1 if the value is not yet known. */ public int Total_Tiles () {return Total_Tiles;} /** Test if a final tile-part of zero (unknown) length was found in the codestream.

If a tile-part of zero length is found in the codestream it must be the last tile-part before the EOC marker. If {@link #EOC_Search} has been set the tile-part will be searched for the final EOC delimiter segment.

@return true if a zero length tile-part was found; false otherwise. */ public boolean Zero_Length_Tile_Part () {return Zero_Length_Tile_Part;} /** Get the total number of quality layers in the codestream.

This value corresponds to the value of the {@link #TOTAL_QUALITY_LAYERS_PARAMETER}.

@return The total number of quality layers in the codestream. This will be -1 if the value is not yet known. */ public int Total_Quality_Layers () {return Total_Quality_Layers;} /** Get the codestream default progression order.

Use the {@link #PROGRESSION_ORDERS} String array to obtain a description of the progression order code.

This value corresponds to the value of the {@link #PROGRESSION_ORDER_PARAMETER}.

@return The codestream progression order code from the COD segment. The value may be one of {@link #LRCP_PROGRESSION_ORDER}, {@link #RLCP_PROGRESSION_ORDER}, {@link #RPCL_PROGRESSION_ORDER}, {@link #PCRL_PROGRESSION_ORDER}, or {@link #CPRL_PROGRESSION_ORDER}. This will be -1 if the value is not yet known. */ public int Progression_Order () {return Progression_Order;} /** Get the total number of resolution levels.

The value is obtained from the first coding style default (COD) segment encountered in the codestream. This is expected to be from the image and tile size (SIZ) segment. But if a tile header is independently parsed and it contains a COD segment then that will be used. N.B.: The value is never obtained from a coding style component (COC) segment. This value corresponds to the value of the {@link #TOTAL_RESOLUTION_LEVELS_PARAMETER}.

The total number of resolution levels is one more than the maximum decomposition level.

@return The total number of resolution levels. This will be -1 if the value is not yet known. */ public int Total_Resolution_Levels () {return Total_Resolution_Levels;} /** Get the form of the codestream transformation.

The value is obtained from the first coding style default (COD) segment encountered in the codestream. This is expected to be from the image and tile size (SIZ) segment. But if a tile header is independently parsed and it contains a COD segment then that will be used. N.B.: The value is never obtained from a coding style component (COC) segment. This value corresponds to the value of the {@link #TRANSFORM_PARAMETER}.

@return The codestream transformation indicator. This will be {@link #TRANSFORM_IRREVERSIBLE} (0) for the 9-7 irreversible filter; it will be {@link #TRANSFORM_REVERSIBLE} (1) for the 5-3 reversible filter. This will be -1 if the value is not yet known. */ public int Transform () {return Transform;} private void Reset_Codestream_Values () { Codestream_Validity = 0; Zero_Length_Tile_Part = false; Reference_Grid_Width = Reference_Grid_Height = Image_Width = Image_Height = Horizontal_Image_Offset = Vertical_Image_Offset = Tile_Width = Tile_Height = Horizontal_Tile_Offset = Vertical_Tile_Offset = -1; Total_Components = Total_Tiles = Total_Quality_Layers = Progression_Order = Total_Resolution_Levels = Transform = -1; Pixel_Width = new int[0]; Pixel_Height = new int[0]; } /*============================================================================== Markers and Marker Segments */ /** Assemble a Parameter Aggregate describing a sequence of codestream segments from a byte array.

A sequence of codestream segments is parsed from the byte data. All segments found are parsed. {@link JPEG2000_Info#Use_Data_Position(boolean) Including data position} parameters is disabled as is {@link JPEG2000_Info#Throw_Illegal_State(boolean) illegal codestream segment organizaion detection}.

@param data A byte array. @param offset The offset where the codestream segments are expected to start. @param length The maximum amount of byte data to be read. However, if the data array ends before length bytes after the starting offset, then only the available bytes will be read. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @return A Parameter Aggregate containing Parameters describing the codestream segment contents. This will be null if the contents of a recognized segment were not available within the data length. */ public static Parameter Segments ( byte[] data, int offset, int length, boolean skip_tiles ) { if (data == null) return null; JPEG2000_Codestream_Info info = new JPEG2000_Codestream_Info (); info.Source (new MemoryCacheImageInputStream (new ByteArrayInputStream (data, offset, length))); info.Source ().setByteOrder (ByteOrder.BIG_ENDIAN); info.Use_Data_Position (false); info.Throw_Illegal_State (false); try {info.Read (0, skip_tiles);} catch (Exception exception) {} return (Parameter)info; } /** Assemble a Parameter Aggregate describing a sequence of codestream segments from a byte array.

The {@link #Skip_Tiles_Default} flag determines if tile segments will be skipped.

@param data A byte array. @param offset The offset where the codestream segments are expected to start. @param length The maximum amount of byte data to be read. However, if the data array ends before length bytes after the starting offset, then only the available bytes will be read. @return A Parameter Aggregate containing Parameters describing the codestream segment contents. This will be null if the contents of a recognized segment were not available within the data length. @see #Segments(byte[], int, int, boolean) */ public static Parameter Segments ( byte[] data, int offset, int length ) {return Segments (data, offset, length, Skip_Tiles_Default);} /** Assemble a Parameter Aggregate describing a sequence of codestream segments from a byte array.

@param data A byte array. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @return A Parameter Aggregate containing Parameters describing the codestream segment contents. This will be null if the contents of a recognized segment were not available within the data length. @see #Segments(byte[], int, int, boolean) */ public static Parameter Segments ( byte[] data, boolean skip_tiles ) {return Segments (data, 0, data.length, skip_tiles);} /** Assemble a Parameter Aggregate describing a sequence of codestream segments from a byte array.

The {@link #Skip_Tiles_Default} flag determines if tile segments will be skipped.

@param data A byte array. @return A Parameter Aggregate containing Parameters describing the codestream segment contents. This will be null if the contents of a recognized segment were not available within the data length. @see #Segments(byte[], int, int, boolean) */ public static Parameter Segments ( byte[] data ) {return Segments (data, 0, data.length, Skip_Tiles_Default);} private void Add_Segments ( Parameter container, long end_position, boolean skip_tiles ) throws IOException, IllegalStateException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JPEG2000_Codestream_Info.Add_Segments: To " + container.Name () + '\n' +" from stream position " + Stream_Position + '\n' +" until stream position " + end_position + '\n' +" skip tiles: " + skip_tiles); Parameter segment = null; long total_tile_parts = 0; if (end_position <= 0) end_position = Long.MAX_VALUE; try { Get_Segments: while (Stream_Position < end_position && (segment = Segment_Parameter ()) != null) { if (segment.Name ().equals (SOT_NAME)) { // Start of tile-part. long tile_part_end_position = 0; Parameter parameter; try { parameter = segment.Find (TILE_PART_LENGTH_PARAMETER); if (parameter != null) // The length of the tile-part. tile_part_end_position = parameter.Value ().long_Data (); if (tile_part_end_position == 0) Zero_Length_Tile_Part = true; else tile_part_end_position = Stream_Position - segment.Find (LENGTH_PARAMETER) .Value ().long_Data () + tile_part_end_position; } catch (PVL_Exception exception) { // Should never happen. segment = null; break; } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" tile-part " + total_tile_parts + " end position = " + tile_part_end_position); if (skip_tiles) { if (Zero_Length_Tile_Part) // Last tile-part before EOC, but unknown length. segment = Search_for_EOC (end_position); else { // Move forward to the end of the tile-part. segment = null; // Don't include the tile-part segment. Stream_Position (tile_part_end_position); } break; } // Add tile-part segments up to and including the Start_of_Data: ++total_tile_parts; while (Stream_Position < end_position && (parameter = Segment_Parameter ()) != null) { try {segment.Add (parameter);} catch (PVL_Exception exception) { // Should never happen. segment = null; break Get_Segments; } if (parameter.Name ().equals (SOD_NAME)) // Start of bitstream data. break; if (tile_part_end_position != 0 && tile_part_end_position <= Stream_Position) // End of tile-part without SOD segment. break; } if (Zero_Length_Tile_Part) { // Last tile-part before EOC, but unknown length. segment = Search_for_EOC (end_position); break; } else { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Stream_Position after last tile-part segment = " + Stream_Position); if (Stream_Position < tile_part_end_position) Stream_Position (tile_part_end_position); } } if (segment.Name ().equals (EOC_NAME)) break; try {container.Add (segment);} catch (PVL_Exception exception) {} segment = null; } } catch (EOFException exception) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" JPEG2000_Codestream_Info.Add_Segments: EOF at stream position " + Stream_Position); } if (! skip_tiles) { // Total tile parts. try { container .Add (new Parameter (TOTAL_TILE_PARTS_PARAMETER) .Value (total_tile_parts) .Comments ("\nTotal number of tile parts found in the codestream.")); if (Total_Tiles > 0 && Total_Tiles > total_tile_parts) container .Add (new Parameter (WARNING_PARAMETER) .Value ("At least " + Total_Tiles + " tile parts were expected.")); } catch (PVL_Exception exception) {} } if (segment != null) { // Final End of Codestream marker. try {container.Add (segment);} catch (PVL_Exception exception) {} } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info.Add_Segments"); } private Parameter Segment_Parameter () throws IOException, IllegalStateException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JPEG2000_Codestream_Info.Segment_Parameter"); long segment_position = Stream_Position; int marker = Read_Unsigned_short (MARKER_PARAMETER); long segment_length = ((marker == SOC_MARKER || marker == SOD_MARKER || marker == EPH_MARKER || marker == EOC_MARKER || (marker >= RESERVED_DELIMITER_MARKER_MIN && marker <= RESERVED_DELIMITER_MARKER_MAX)) ? // Delimiting marker; no segment. 2 : // Segment length. Read_Unsigned_short (Marker_Name (marker) + ' ' + LENGTH_PARAMETER) + 2), end_position = segment_position + segment_length; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" JPEG2000_Codestream_Info.Segment_Parameter: \"" + Marker_Name (marker) + "\" (0x" + Integer.toHexString (marker) + ") -\n" +" Position = " + segment_position + '\n' +" Length = " + segment_length); Parameter segment = new Parameter (Marker_Name (marker)); try { segment .Add (new Parameter (MARKER_PARAMETER) .Value (new Value (marker).Base (16))); if (Use_Data_Position) segment .Add (new Parameter (POSITION_PARAMETER) .Value (new Value (segment_position).Units ("byte offset"))); segment .Add (new Parameter (LENGTH_PARAMETER) .Value (new Value (segment_length).Units ("bytes"))); } catch (PVL_Exception exception) {} if (Codestream_Validity == 0) { // The first marker must be the SOC. if (marker != SOC_MARKER) { if (Throw_Illegal_State) throw new IllegalStateException (ID + '\n' + "Not a valid JPEG2000 codestream: No initial " + Marker_Name (SOC_MARKER) + " marker."); } else Codestream_Validity = SOC_FOUND; } else if (Codestream_Validity == SOC_FOUND) { // The second marker must be the SIZ. if (marker != SIZ_MARKER) { if (Throw_Illegal_State) throw new IllegalStateException (ID + '\n' + "Not a valid JPEG2000 codestream: Missing " + Marker_Name (SIZ_MARKER) + " marker."); } else Codestream_Validity |= SIZ_FOUND; } if (marker < RESERVED_DELIMITER_MARKER_MIN || marker > RESERVED_DELIMITER_MARKER_MIN) { switch (marker) { // Delimiting markers. case EOC_MARKER: Codestream_Validity |= EOC_FOUND; case SOC_MARKER: case SOD_MARKER: case EPH_MARKER: break; // Segments. case SOT_MARKER: SOT_Parameters (segment); break; case SIZ_MARKER: SIZ_Parameters (segment); break; case COD_MARKER: COD_Parameters (segment); Codestream_Validity |= COD_FOUND; break; case COC_MARKER: COC_Parameters (segment, end_position); break; case RGN_MARKER: RGN_Parameters (segment, end_position); break; case QCD_MARKER: QCD_Parameters (segment, end_position); Codestream_Validity |= QCD_FOUND; break; case QCC_MARKER: QCC_Parameters (segment, end_position); break; case POC_MARKER: POC_Parameters (segment, end_position); break; case TLM_MARKER: TLM_Parameters (segment, end_position); break; case PLM_MARKER: PLM_Parameters (segment, end_position); break; case PLT_MARKER: PLT_Parameters (segment, end_position); break; case PPM_MARKER: PPM_Parameters (segment, end_position); break; case PPT_MARKER: PPT_Parameters (segment, end_position); break; case SOP_MARKER: SOP_Parameters (segment); break; case CRG_MARKER: CRG_Parameters (segment, end_position); break; case COM_MARKER: COM_Parameters (segment, end_position); break; } } if (Stream_Position < end_position) Stream_Position (end_position); else if (Throw_Illegal_State && Stream_Position > end_position) throw new IllegalStateException (ID + '\n' + "Invalid " + segment.Name () + " contents -\n" + " expected length " + segment_length + " but used length " + (Stream_Position - segment_position)); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (((segment == null) ? "" : (segment.Description ()) + '\n') +"<<< JPEG2000_Codestream_Info.Segment_Parameter: " + segment); return segment; } private void SOT_Parameters ( Parameter segment ) throws IOException { try { segment .Add (new Parameter (TILE_INDEX_PARAMETER) .Value (Read_Unsigned_short (segment.Name () + ' ' + TILE_INDEX_PARAMETER))) .Add (new Parameter (TILE_PART_LENGTH_PARAMETER) .Value (new Value (Read_Unsigned_int (segment.Name () + ' ' + TILE_PART_LENGTH_PARAMETER)).Units ("bytes"))) .Add (new Parameter (TILE_PART_INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + TILE_PART_INDEX_PARAMETER))) .Add (new Parameter (TOTAL_TILE_PARTS_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + TOTAL_TILE_PARTS_PARAMETER))); } catch (PVL_Exception exception) {} } private void SIZ_Parameters ( Parameter segment ) throws IOException { try { segment .Add (new Parameter (CAPABILITY_PARAMETER) .Value (new Value (Read_Unsigned_short (segment.Name () + ' ' + CAPABILITY_PARAMETER)) .Base (16))) .Add (new Parameter (REFERENCE_GRID_WIDTH_PARAMETER) .Value (Reference_Grid_Width = Read_Unsigned_int (segment.Name () + ' ' + REFERENCE_GRID_WIDTH_PARAMETER))) .Add (new Parameter (REFERENCE_GRID_HEIGHT_PARAMETER) .Value (Reference_Grid_Height = Read_Unsigned_int (segment.Name () + ' ' + REFERENCE_GRID_HEIGHT_PARAMETER))) .Add (new Parameter (HORIZONTAL_IMAGE_OFFSET_PARAMETER) .Value (Horizontal_Image_Offset = Read_Unsigned_int (segment.Name () + ' ' + HORIZONTAL_IMAGE_OFFSET_PARAMETER))) .Add (new Parameter (VERTICAL_IMAGE_OFFSET_PARAMETER) .Value (Vertical_Image_Offset = Read_Unsigned_int (segment.Name () + ' ' + VERTICAL_IMAGE_OFFSET_PARAMETER))) .Add (new Parameter (TILE_WIDTH_PARAMETER) .Value (Tile_Width = Read_Unsigned_int (segment.Name () + ' ' + TILE_WIDTH_PARAMETER))) .Add (new Parameter (TILE_HEIGHT_PARAMETER) .Value (Tile_Height = Read_Unsigned_int (segment.Name () + ' ' + TILE_HEIGHT_PARAMETER))) .Add (new Parameter (HORIZONTAL_TILE_OFFSET_PARAMETER) .Value (Horizontal_Tile_Offset = Read_Unsigned_int (segment.Name () + ' ' + HORIZONTAL_TILE_OFFSET_PARAMETER))) .Add (new Parameter (VERTICAL_TILE_OFFSET_PARAMETER) .Value (Vertical_Tile_Offset = Read_Unsigned_int (segment.Name () + ' ' + VERTICAL_TILE_OFFSET_PARAMETER))) .Add (new Parameter (TOTAL_COMPONENTS_PARAMETER) .Value (Total_Components = Read_Unsigned_short (segment.Name () + ' ' + TOTAL_COMPONENTS_PARAMETER))); Image_Width = Reference_Grid_Width - Horizontal_Image_Offset; Image_Height = Reference_Grid_Height - Vertical_Image_Offset; Value Ssiz = new Value ().Type (Value.SEQUENCE), XRsiz = new Value ().Type (Value.SEQUENCE), YRsiz = new Value ().Type (Value.SEQUENCE); for (int component = 0; component < Total_Components; ++component) { Ssiz.Add (new Value (Read_byte (segment.Name () + ' ' + VALUE_BITS_PARAMETER + ' ' + component))); XRsiz.Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + HORIZONTAL_SAMPLE_SPACING_PARAMETER + ' ' + component))); YRsiz.Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + VERTICAL_SAMPLE_SPACING_PARAMETER + ' ' + component))); } segment .Add (Value_Bits_Parameter (Ssiz)) .Add (new Parameter (HORIZONTAL_SAMPLE_SPACING_PARAMETER) .Value (XRsiz)) .Add (new Parameter (VERTICAL_SAMPLE_SPACING_PARAMETER) .Value (YRsiz)); Pixel_Width = int_Array (XRsiz); Pixel_Height = int_Array (YRsiz); Total_Tiles = (int)( Math.ceil (((double)Reference_Grid_Width - Horizontal_Tile_Offset) / Tile_Width) * Math.ceil (((double)Reference_Grid_Height - Vertical_Tile_Offset) / Tile_Height)); } catch (PVL_Exception exception) {} } private void COD_Parameters ( Parameter segment ) throws IOException { try { // Scod: int Scod = Read_Unsigned_byte (segment.Name () + ' ' + CODING_STYLE_PARAMETER); segment.Add (new Parameter (CODING_STYLE_PARAMETER) .Comments ("\nEntropy coder precincts:\n" + " Precinct size " + (((Scod & ENTROPY_CODER_FLAG) == 0) ? "= 32768 x 32768\n" : ("defined in the " + PRECINCT_SIZE_PARAMETER + " parameter.\n")) + (((Scod & SOP_FLAG) == 0) ? " No " : " ") + "SOP marker segments used\n" + (((Scod & EPH_FLAG) == 0) ? " No " : " ") + "EPH marker used") .Value (new Value (Scod) .Base (16))); // SGcod: Progression_Order = Read_Unsigned_byte (segment.Name () + ' ' + PROGRESSION_ORDER_PARAMETER); segment .Add (new Parameter (PROGRESSION_ORDER_PARAMETER) .Comments ("\nProgression order:\n " + ((Progression_Order < PROGRESSION_ORDERS.length) ? PROGRESSION_ORDERS[Progression_Order] : "Unknown")) .Value (Progression_Order)) .Add (new Parameter (TOTAL_QUALITY_LAYERS_PARAMETER) .Value (Total_Quality_Layers = Read_Unsigned_short (segment.Name () + ' ' + TOTAL_QUALITY_LAYERS_PARAMETER))) .Add (new Parameter (MULTIPLE_COMPONENT_TRANSFORM_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + MULTIPLE_COMPONENT_TRANSFORM_PARAMETER))); // SPcod: Coding_Style_Parameters (segment, (Scod & ENTROPY_CODER_FLAG) != 0); // Set the default (highest level) resolution levels and transform type. Parameter parameter; if (Total_Resolution_Levels < 0 && (parameter = segment.Find (TOTAL_RESOLUTION_LEVELS_PARAMETER)) != null) Total_Resolution_Levels = (int)(parameter.Value ().long_Data ()); if (Transform < 0 && (parameter = segment.Find (TRANSFORM_PARAMETER)) != null) Transform = (int)(parameter.Value ().long_Data ()); } catch (PVL_Exception exception) {} } private void COC_Parameters ( Parameter segment, long end_position ) throws IOException { try { /* CAUTION: The size of the COMPONENT_INDEX_PARAMETER is dependent on the value of Total_Components. If Total_Components has not yet been set from a COD segment it is not possible to parse this segment. */ if (Total_Components < 0) { segment.Add (new Parameter (WARNING_PARAMETER) .Value ("Can not parse COC segment because total components unknown.")); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 0, end_position - Stream_Position); } else { segment.Add (new Parameter (COMPONENT_INDEX_PARAMETER) .Value ((Total_Components < 257) ? Read_Unsigned_byte (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER))); // Scoc: int Scoc = Read_Unsigned_byte (segment.Name () + ' ' + CODING_STYLE_PARAMETER); segment.Add (new Parameter (CODING_STYLE_PARAMETER) .Comments ("\nEntropy coder precincts:\n" + " Precinct size " + (((Scoc & ENTROPY_CODER_FLAG) == 0) ? "= 32768 x 32768" : ("defined in the " + PRECINCT_SIZE_PARAMETER + " parameter."))) .Value (new Value (Scoc) .Base (16))); // SPcoc: Coding_Style_Parameters (segment, (Scoc & ENTROPY_CODER_FLAG) != 0); } } catch (PVL_Exception exception) {} } private void Coding_Style_Parameters ( Parameter segment, boolean has_precinct_sizes ) throws IOException { try { int levels, datum; segment .Add (new Parameter (TOTAL_RESOLUTION_LEVELS_PARAMETER) .Value (levels = Read_Unsigned_byte (segment.Name () + ' ' + TOTAL_RESOLUTION_LEVELS_PARAMETER) + 1)) .Add (new Parameter (CODE_BLOCK_WIDTH_PARAMETER) .Value (1 << ((datum = Read_Unsigned_byte (segment.Name () + ' ' + CODE_BLOCK_WIDTH_PARAMETER)) + 2)) .Comments ("\nCode-block width exponent offset " + datum + '.')) .Add (new Parameter (CODE_BLOCK_HEIGHT_PARAMETER) .Value (1 << ((datum = Read_Unsigned_byte (segment.Name () + ' ' + CODE_BLOCK_HEIGHT_PARAMETER)) + 2)) .Comments ("\nCode-block height exponent offset " + datum + '.')); datum = Read_Unsigned_byte (segment.Name () + ' ' + CODE_BLOCK_STYLE_PARAMETER); segment.Add (new Parameter (CODE_BLOCK_STYLE_PARAMETER) .Comments ("\nCode-block style:\n" + (((datum & SELECTIVE_ARITHMETIC_BYPASS_FLAG) == 0) ? " No s" : " S") + "elective arithmetic coding bypass.\n" + (((datum & RESET_CONTEXT_PROBABILITIES) == 0) ? " No r" : " R") + "eset of context probabilities on coding pass boundaries.\n" + (((datum & TERMINATION_FLAG) == 0) ? " No t" : " T") + "ermination on each coding pass.\n" + (((datum & VERTICALLY_CAUSAL_CONTEXT_FLAG) == 0) ? " No v" : " V") + "erticallly causal context.\n" + (((datum & PREDICTABLE_TERMINATION_FLAG) == 0) ? " No p" : " P") + "redictable termination.\n" + (((datum & SEGMENTATION_SYMBOLS_FLAG) == 0) ? " No s" : " S") + "egmentation symbols are used.") .Value (new Value (datum).Base (16))); datum = Read_Unsigned_byte (segment.Name () + ' ' + TRANSFORM_PARAMETER); segment.Add (new Parameter (TRANSFORM_PARAMETER) .Comments ("\nWavelet transformation used:\n " + ((datum == TRANSFORM_IRREVERSIBLE) ? "9-7 irreversible filter." : ((datum == TRANSFORM_REVERSIBLE) ? "5-3 reversible filter." : "Unknown"))) .Value (datum)); if (has_precinct_sizes) { Value list = new Value ().Type (Value.SEQUENCE), pair; for (int level = 0; level < levels; ++level) { pair = new Value ().Type (Value.SEQUENCE); datum = Read_Unsigned_byte (segment.Name () + ' ' + PRECINCT_SIZE_PARAMETER + ' ' + level); pair .Add (new Value (1 << (datum & 0x0F))) .Add (new Value (1 << ((datum & 0xF0) >> 4))); list.Add (pair); } segment.Add (new Parameter (PRECINCT_SIZE_PARAMETER) .Comments ("\nPrecinct (width, height) by resolution level.") .Value (list)); } } catch (PVL_Exception exception) {} } private void RGN_Parameters ( Parameter segment, long end_position ) throws IOException { try { /* CAUTION: The size of the COMPONENT_INDEX_PARAMETER is dependent on the value of Total_Components. If Total_Components has not yet been set from a COD segment it is not possible to parse this segment. */ if (Total_Components < 0) { segment.Add (new Parameter (WARNING_PARAMETER) .Value ("Can not parse RGN segment because total components unknown.")); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 0, end_position - Stream_Position); } else segment .Add (new Parameter (COMPONENT_INDEX_PARAMETER) .Value ((Total_Components < 257) ? Read_Unsigned_byte (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER))) .Add (new Parameter (ROI_STYLE_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + ROI_STYLE_PARAMETER))) .Add (new Parameter (IMPLICIT_SHIFT_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + IMPLICIT_SHIFT_PARAMETER))); } catch (PVL_Exception exception) {} } private void QCD_Parameters ( Parameter segment, long end_position ) throws IOException {Quantization_Parameters (segment, end_position);} private void QCC_Parameters ( Parameter segment, long end_position ) throws IOException { try { /* CAUTION: The size of the COMPONENT_INDEX_PARAMETER is dependent on the value of Total_Components. If Total_Components has not yet been set from a COD segment it is not possible to parse this segment. */ if (Total_Components < 0) { segment.Add (new Parameter (WARNING_PARAMETER) .Value ("Can not parse QCC segment because total components unknown.")); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 0, end_position - Stream_Position); } else { segment.Add (new Parameter (COMPONENT_INDEX_PARAMETER) .Value ((Total_Components < 257) ? Read_Unsigned_byte (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER))); Quantization_Parameters (segment, end_position); } } catch (PVL_Exception exception) {} } private void Quantization_Parameters ( Parameter segment, long end_position ) throws IOException { try { int datum = Read_Unsigned_byte (segment.Name () + ' ' + QUANTIZATION_STYLE_PARAMETER), style = datum & 3; segment.Add (new Parameter (QUANTIZATION_STYLE_PARAMETER) .Comments ("\nQuantization:\n " + ((style == NO_QUANTIZATION) ? "None" : ((style == QUANTIZATION_SCALAR_DERIVED) ? "Scalar derived; values signalled for N-subL LL sub-band only." : ((style == QUANTIZATION_SCALAR_EXPOUNDED) ? "Scalar expounded; values signalled for each sub-band." : "Unknown")))) .Value (style)); segment.Add (new Parameter (TOTAL_GUARD_BITS_PARAMETER) .Value ((datum & 0xE0) >> 5)); Value list = new Value ().Type (Value.SEQUENCE), entry; while (Stream_Position < end_position) { // Exponent datum = Read_Unsigned_byte (segment.Name () + ' ' + STEP_SIZE_PARAMETER); if (style == NO_QUANTIZATION) entry = new Value ((datum & 0xF8) >> 3); else { // Mantissa entry = new Value ().Type (Value.SEQUENCE); entry.Add (new Value ((datum & 0xF8) >> 3)); datum <<= 8; datum |= Read_Unsigned_byte (segment.Name () + ' ' + STEP_SIZE_PARAMETER); entry.Add (new Value (datum & 0x07FF)); } list.Add (entry); } segment.Add (new Parameter (STEP_SIZE_PARAMETER) .Comments ("\n" + ((style == NO_QUANTIZATION) ? "Reversible transform dynamic range exponent" : "Irreversible transform quantization step size (exponent, mantissa)") + " by sub-band.") .Value (list)); } catch (PVL_Exception exception) {} } private void POC_Parameters ( Parameter segment, long end_position ) throws IOException { try { /* CAUTION: The size of the COMPONENT_INDEX_PARAMETER is dependent on the value of Total_Components. If Total_Components has not yet been set from a COD segment it is not possible to parse this segment. */ if (Total_Components < 0) { segment.Add (new Parameter (WARNING_PARAMETER) .Value ("Can not parse POC segment because total components unknown.")); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 0, end_position - Stream_Position); } else { Value levels = new Value ().Type (Value.SEQUENCE), components = new Value ().Type (Value.SEQUENCE), layers = new Value ().Type (Value.SEQUENCE), progression_orders = new Value ().Type (Value.SEQUENCE), levels_range, components_range; int datum; while (Stream_Position < end_position) { levels_range = new Value ().Type (Value.SEQUENCE) .Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + LEVEL_INDEX_PARAMETER))); components_range = new Value ().Type (Value.SEQUENCE) .Add (new Value ((Total_Components < 257) ? Read_Unsigned_byte (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER))); layers.Add (new Value (Read_Unsigned_short (segment.Name () + ' ' + LAYER_INDEX_PARAMETER))); levels_range.Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + LEVEL_INDEX_PARAMETER))); levels.Add (levels_range); datum = (Total_Components < 257) ? Read_Unsigned_byte (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + COMPONENT_INDEX_PARAMETER); if (datum == 0) datum = 256; components_range.Add (new Value (datum)); components.Add (components_range); progression_orders.Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + PROGRESSION_ORDER_PARAMETER))); } String comment = "\nIndex (start, end] ranges."; segment .Add (new Parameter (LEVEL_INDEX_PARAMETER) .Comments (comment) .Value (levels)) .Add (new Parameter (COMPONENT_INDEX_PARAMETER) .Comments (comment) .Value (components)) .Add (new Parameter (LAYER_INDEX_PARAMETER) .Value (layers)) .Add (new Parameter (PROGRESSION_ORDER_PARAMETER) .Value (progression_orders)); } } catch (PVL_Exception exception) {} } private void TLM_Parameters ( Parameter segment, long end_position ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + INDEX_PARAMETER))); int index_size, length_size = Read_Unsigned_byte (segment.Name () + ' ' + TILE_INDEX_SIZE_PARAMETER + " and " + TILE_PART_LENGTH_SIZE_PARAMETER); length_size >>= 4; index_size = length_size & 0x3; length_size = (((length_size >> 2) & 0x3) + 1) * 2; segment .Add (new Parameter (TILE_INDEX_SIZE_PARAMETER) .Value (new Value (index_size).Units ("bytes"))) .Add (new Parameter (TILE_PART_LENGTH_SIZE_PARAMETER) .Value (new Value (length_size).Units ("bytes"))); Value indices = new Value ().Type (Value.SEQUENCE), lengths = new Value ().Type (Value.SEQUENCE).Units ("bytes"); while (Stream_Position < end_position) { if (index_size != 0) indices.Add (new Value ((index_size == 1) ? Read_Unsigned_byte (segment.Name () + ' ' + TILE_INDEX_PARAMETER) : Read_Unsigned_short (segment.Name () + ' ' + TILE_INDEX_PARAMETER))); lengths.Add (new Value ((length_size == 2) ? Read_Unsigned_short (segment.Name () + ' ' + TILE_PART_LENGTH_PARAMETER) : Read_Unsigned_int (segment.Name () + ' ' + TILE_PART_LENGTH_PARAMETER))); } if (index_size != 0) segment.Add (new Parameter (TILE_INDEX_PARAMETER) .Value (indices)); segment.Add (new Parameter (TILE_PART_LENGTH_PARAMETER) .Value (lengths)); } catch (PVL_Exception exception) {} } private void PLM_Parameters ( Parameter segment, long end_position ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + INDEX_PARAMETER))); Value lengths; if (PLM_Packet_Length_Parameter == null) // First PLM segment; add the new packet lengths parameter and array. segment.Add (PLM_Packet_Length_Parameter = new Parameter (PACKET_LENGTH_PARAMETER) .Value (lengths = new Value ().Type (Value.SEQUENCE).Units ("bytes"))); else { // Continuation PLM segment; append to the existing lengths array. lengths = PLM_Packet_Length_Parameter.Value (); segment.Add (new Parameter (CONTINUATION_PARAMETER) .Value ("true")); } long datum; while (Stream_Position < end_position) { if (PLM_Packet_Length_Bytes_Remaining == 0) PLM_Packet_Length_Bytes_Remaining = Read_Unsigned_byte (segment.Name () + " byte count"); datum = Read_Unsigned_byte (segment.Name () + ' ' + PACKET_LENGTH_PARAMETER); --PLM_Packet_Length_Bytes_Remaining; PLM_Packet_Length <<= 7; PLM_Packet_Length |= datum & 0x7F; if (PLM_Packet_Length_Bytes_Remaining == 0) { lengths.Add (new Value (PLM_Packet_Length)); PLM_Packet_Length = 0; } } } catch (PVL_Exception exception) {} } private void PLT_Parameters ( Parameter segment, long end_position ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + INDEX_PARAMETER))); Value lengths; if (PLT_Packet_Length_Parameter == null) // First PLT segment; add the new packet lengths parameter and array. segment.Add (PLT_Packet_Length_Parameter = new Parameter (PACKET_LENGTH_PARAMETER) .Value (lengths = new Value ().Type (Value.SEQUENCE).Units ("bytes"))); else { // Continuation PLT segment; append to the existing lengths array. lengths = PLT_Packet_Length_Parameter.Value (); segment.Add (new Parameter (CONTINUATION_PARAMETER) .Value ("true")); } long datum; while (Stream_Position < end_position) { datum = Read_Unsigned_byte (segment.Name () + ' ' + PACKET_LENGTH_PARAMETER); PLT_Packet_Length <<= 7; PLT_Packet_Length |= datum & 0x7F; if ((PLT_Packet_Length_Bytes_Remaining = (datum & 0x80)) == 0) { lengths.Add (new Value (PLT_Packet_Length)); PLT_Packet_Length = 0; } } } catch (PVL_Exception exception) {} } private void PPM_Parameters ( Parameter segment, long end_position ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + INDEX_PARAMETER))); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 1, end_position - Stream_Position); } catch (PVL_Exception exception) {} } private void PPT_Parameters ( Parameter segment, long end_position ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_byte (segment.Name () + ' ' + INDEX_PARAMETER))); if (Use_Data_Position) Data_Position_Parameters (segment, end_position); else Data_Offset_Parameters (segment, 1, end_position - Stream_Position); } catch (PVL_Exception exception) {} } private void SOP_Parameters ( Parameter segment ) throws IOException { try { segment.Add (new Parameter (INDEX_PARAMETER) .Value (Read_Unsigned_short (segment.Name () + ' ' + INDEX_PARAMETER))); } catch (PVL_Exception exception) {} } private void CRG_Parameters ( Parameter segment, long end_position ) throws IOException { try { Value horizontal_offsets = new Value ().Type (Value.SEQUENCE), vertical_offsets = new Value ().Type (Value.SEQUENCE); while (Stream_Position < end_position) { horizontal_offsets.Add (new Value (Read_Unsigned_short (segment.Name () + ' ' + HORIZONTAL_COMPONENT_OFFSET_PARAMETER))); vertical_offsets.Add (new Value (Read_Unsigned_short (segment.Name () + ' ' + VERTICAL_COMPONENT_OFFSET_PARAMETER))); } segment .Add (new Parameter (HORIZONTAL_COMPONENT_OFFSET_PARAMETER) .Value (horizontal_offsets) .Comments ("\nOffset in units of 1/65536 of the " + HORIZONTAL_SAMPLE_SPACING_PARAMETER + " by component.")) .Add (new Parameter (VERTICAL_COMPONENT_OFFSET_PARAMETER) .Value (vertical_offsets) .Comments ("\nOffset in units of 1/65536 of the " + VERTICAL_SAMPLE_SPACING_PARAMETER + " by component.")); } catch (PVL_Exception exception) {} } private void COM_Parameters ( Parameter segment, long end_position ) throws IOException { try { int datum; segment.Add (new Parameter (DATA_TYPE_PARAMETER) .Value (datum = Read_Unsigned_short (segment.Name () + ' ' + DATA_TYPE_PARAMETER))); if (datum == DATA_TYPE_TEXT) segment.Add (new Parameter (TEXT_DATA_PARAMETER) .Value (new Value (Read_String (segment.Name (), end_position)) .Type (Value.TEXT))); else { // DATA_TYPE_BINARY Value sequence = new Value ().Type (Value.SEQUENCE); for (long index = Stream_Position (); index < end_position; ++index) sequence.Add (new Value (Read_Unsigned_byte (segment.Name () + ' ' + BINARY_DATA_PARAMETER)).Base (16)); segment.Add (new Parameter (BINARY_DATA_PARAMETER) .Value (sequence)); } } catch (PVL_Exception exception) {} } private Parameter Search_for_EOC ( long end_position ) throws IOException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JPEG2000_Codestream_Info.Search_for_EOC:\n" +" from position " + Stream_Position + '\n' +" to position " + end_position); if (! EOC_Search) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" EOC_Search disabled\n" +"<<< JPEG2000_Codestream_Info.Search_for_EOC"); return null; } Parameter segment = null; while (Stream_Position < end_position) { if (Read_Unsigned_byte (EOC_NAME + " MSB") == (EOC_MARKER >>> 8) && Read_Unsigned_byte (EOC_NAME + " LSB") == (EOC_MARKER & 0xFF)) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" EOC found at position " + (Stream_Position - 2)); Codestream_Validity |= EOC_FOUND; segment = new Parameter (EOC_NAME); try { segment .Add (new Parameter (MARKER_PARAMETER) .Value (new Value (EOC_MARKER).Base (16))); if (Use_Data_Position) segment .Add (new Parameter (POSITION_PARAMETER) .Value (new Value (Stream_Position - 2) .Units ("byte offset"))); segment .Add (new Parameter (LENGTH_PARAMETER) .Value (new Value (2).Units ("bytes"))); } catch (PVL_Exception exception) {} break; } } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JPEG2000_Codestream_Info.Search_for_EOC"); return segment; } /*============================================================================== Helpers */ public static String Marker_Name ( int marker ) throws IOException { String string = (String)Marker_Names.get (new Integer (marker)); if (string == null) string = UNKNOWN; return string; } private IOException IO_Error ( String message, Exception exception ) { if (message == null) message = ID; else if (message.indexOf (ID) < 0) message = ID + '\n' + message; if (exception != null) message += "\n" + exception.getMessage (); IOException except = new IOException (message); if (exception != null) { Throwable cause = exception.getCause (); if (cause != null) { try {except.initCause (cause);} catch (IllegalStateException e) {} } } return except; } private IOException IO_Error ( String message ) {return IO_Error (message, null);} /*============================================================================== Main */ //! Exit status values. public static final int SUCCESS = 0, SYNTAX_ERROR = 1, IO_EXCEPTION = 2, NO_INFO = 3, PVL_ERROR = 4; /** Report the JPEG2000 codestream segments found in a raw codestream file.

@param arguments The arguments String array. @see #Usage() */ public static void main ( String[] arguments ) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println ("*** " + ID); boolean skip_tiles = true, EOC_search = false, GUI = false; String source = null; Vector names = new Vector (), patterns = new Vector (); for (int argument = 0; argument < arguments.length; ++argument) { if (arguments[argument].startsWith ("-") && arguments[argument].length () > 1) { switch (arguments[argument].charAt (1)) { case 'N': case 'n': int index = arguments[argument].indexOf ('_'); if (index < 0) { System.out.println ("Unknown option: " + arguments[argument]); Usage (); } switch (arguments[argument].charAt (++index)) { case 'T': case 't': skip_tiles = true; break; case 'E': case 'e': EOC_search = false; break; default: System.out.println ("Unknown option: " + arguments[argument]); } break; case 'T': case 't': skip_tiles = false; break; case 'E': case 'e': EOC_search = true; break; case 'F': case 'f': if (++argument == arguments.length) Usage (); names.add (arguments[argument]); break; case 'P': case 'p': if (++argument == arguments.length) Usage (); patterns.add (arguments[argument]); break; case 'G': case 'g': GUI = true; break; default: System.out.println ("Unknown option: " + arguments[argument]); case 'H': case 'h': Usage (); } } else if (source == null) source = arguments[argument]; else { System.err.println ("Only one source, please."); Usage (); } } if (source == null) { System.err.println ("A source must be specified."); Usage (); } // Get the Info. JPEG2000_Codestream_Info info = null; JPEG2000_Codestream_Info.EOC_Search = EOC_search; try {info = new JPEG2000_Codestream_Info (source, skip_tiles);} catch (Exception exception) { System.out.println (exception.getMessage ()); System.exit (IO_EXCEPTION); } // Report the Info. if (info == null) { System.out.println ("No JPEG2000 Codestream Info"); System.exit (NO_INFO); } int validity = info.Codestream_Validity (); boolean EOC_missing = ((! info.Skip_Tiles ()) && ((validity & JPEG2000_Codestream_Info.EOC_FOUND) == 0)) || (info.Zero_Length_Tile_Part () && JPEG2000_Codestream_Info.EOC_Search); if (! info.Is_Complete_Codestream () || EOC_missing) { System.out.println (">>> WARNING <<< Incomplete JPEG2000 codestream.\n"); if ((validity & JPEG2000_Codestream_Info.SOC_FOUND) == 0) System.out.println (" Start of Codestream marker not found."); if ((validity & JPEG2000_Codestream_Info.SIZ_FOUND) == 0) System.out.println (" Size marker not found."); if ((validity & JPEG2000_Codestream_Info.COD_FOUND) == 0) System.out.println (" Coding Style Default marker not found."); if ((validity & JPEG2000_Codestream_Info.QCD_FOUND) == 0) System.out.println (" Quantization Default marker not found."); if (EOC_missing) System.out.println (" End of Codestream marker not found."); System.out.println (); } if (! names.isEmpty () || ! patterns.isEmpty ()) { Parameter select = new Parameter (), parameter; Selector criteria; String name; int root_name_length = info.Name ().length () + 1; Iterator list; if (! names.isEmpty ()) { criteria = new Selection ().Name (true).Specific (false); list = names.iterator (); while (list.hasNext ()) { select.Name ((String)list.next ()); parameter = null; while ((parameter = info.Find (select, criteria, parameter)) != null) { name = parameter.Name (); parameter.Name (parameter.Path_to_Name () .substring (root_name_length) + name); try {parameter.Write ();} catch (Exception exception) { System.out.println ("PVL error -\n" + exception.getMessage () + "\n"); System.exit (PVL_ERROR); } parameter.Name (name); } } } if (! patterns.isEmpty ()) { criteria = new Selection ().Name (true).Pattern_Match (true); list = patterns.iterator (); while (list.hasNext ()) { select.Name ((String)list.next ()); parameter = null; while ((parameter = info.Find (select, criteria, parameter)) != null) { name = parameter.Name (); parameter.Name (parameter.Path_to_Name () .substring (root_name_length) + name); try {parameter.Write ();} catch (Exception exception) { System.out.println ("PVL error -\n" + exception.getMessage () + "\n"); System.exit (PVL_ERROR); } parameter.Name (name); } } } } else if (GUI) { Parameter_View view = new Parameter_View (info); view.setVisible (true); } else { System.out.println (info.Description ()); System.exit (SUCCESS); } } /** Print the command line usage syntax.

Usage

JPEG2000_Codestream_Info [-[No_]Tiles] [-Gui] [-Find <parameter>] [-Pattern <regex>] <source>

Description

The contents of the specified file are scanned for raw (not contained within a JP2 file) JPEG2000 codestream segments. The information found is reported in the form of PVL parameters containing all the data element values.

Options

-[No_]Tiles

Tiles segment parameters are to be included (or not). The default is not to include tile segment parameters.

-Gui

The information PVL is provided in a GUI viewer. The default is to provide a terminal listing.

-Find <parameter>

The named parameter is listed. This option may be repeated to obtain a listing of all parameters specified.

-Pattern <regex>

The parameters with names that match the regular expression are listed. This option may also be repeated.

N.B.This method always results in a System.exit with a status of 1. */ public static void Usage () { System.out.println ( ID + '\n' + "Usage: JP2_Info [] \n" + " The contents of the specified file are scanned\n" + " for raw (not in a JP2 file) JPEG2000 codestream segments.\n" + " The information found is reported in the form of\n" + " PVL parameters containing all the data element values.\n" + "\n" + "Options -\n" + " -[No_]Tiles\n" + " Tiles segment parameters are to be included (or not).\n" + " The default is not to include tile segment parameters.\n" + " -[No_]EOC_search\n" + " Search a final tile-part of unknown length for the EOC marker.\n" + " The default is not to search.\n" + " -Gui\n" + " The information PVL is provided in a GUI viewer.\n" + " The default is to provide a terminal listing.\n" + " -Find \n" + " The named parameter is listed. This option may be repeated\n" + " to obtain a listing of all parameters specified.\n" + " -Pattern \n" + " The parameters with names that match the regular expression\n" + " are listed. This option may also be repeated.\n" + " -Help\n" + " Print this usage description and exit.\n" ); System.exit (SYNTAX_ERROR); } } pirl-2.3.8/PIRL/Image_Tools/Multibuf_ImageInputStream.java0000644000175000017500000004551111742733714023263 0ustar mathieumathieu/* Multibuf_ImageInputStream PIRL CVS ID: Multibuf_ImageInputStream.java,v 1.8 2012/04/16 06:10:20 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStreamImpl; import java.nio.ByteBuffer; import java.util.Collections; import java.util.List; import java.util.Vector; import java.lang.IllegalArgumentException; /** An Multibuf_ImageInputStream provides an ImageInputStream interface to a List of ByteBuffers.

The buffer list is thread-safe.

@author Bradford Castalia UA/PIRL @version 1.8 @see javax.imageio.stream.ImageInputStream */ public class Multibuf_ImageInputStream extends ImageInputStreamImpl { public static final String ID = "PIRL.Image_Tools.Multibuf_ImageInputStream (1.8 2012/04/16 06:10:20)"; /*------------------------------------------------------------------------------ Buffer Management */ /** The list of ByteBuffers that provide the data source. */ private List Data_Buffers = Collections.synchronizedList (new Vector ()); /** Current image data buffer.

The Data_Buffer contains the current data accessed by the ImageInputStream interface. It's position is where the next ImageInputStream read byte will be obtained; this is equivalent to the virtual stream location. It's limit is the end of valid source data in the buffer (the buffer may not be full to capacity). */ protected ByteBuffer Data_Buffer = null; /** The index of the Data_Buffer in the Data_Buffers. */ private int Buffer_Index = 0; /* !!! Workaround !!! The reset mehod of ImageInputStreamImpl throws an exception if the position popped from the markByteStack is located before the flushedPos (beginning of the buffer). This implementation allows seeking to a location before the beginning of the buffer. The workaround is to leave the flushedPos at zero and use an alternate variable for the buffer stream location. */ /** Virtual stream location of the Data_Buffer

The first byte of the buffer is located at Buffer_Location offset in the stream. */ protected long Buffer_Location = 0; /** Total size (bytes) of all buffers in the list that constitute the source data stream. */ private long Stream_Size = 0; // Sum of all buffer content amounts. //------------------------------------------------------------------------------ // DEBUG control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Multibuf_ImageInputStream on a List of ByteBuffers.

@param buffers The List of ByteBuffers to use to use as the data source in the order they occur in the List. @throws IllegalArgumentException If the List contains a non-null Object that is not a.ByteBuffer instance. @see #Add(ByteBuffer) */ public Multibuf_ImageInputStream ( List buffers ) throws IllegalArgumentException { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Multibuf_ImageInputStream: " + buffers); if (buffers != null) { for (int index = 0; index < buffers.size (); index++) { Object buffer = buffers.get (index); if (buffer != null && ! (buffer instanceof ByteBuffer)) throw new IllegalArgumentException (ID + '\n' + "Can't construct a Multibuf_ImageInputStream from a List\n" + "where entry " + index + '/' + buffers.size () + " is not a ByteBuffer."); Add ((ByteBuffer)buffer); } } if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Multibuf_ImageInputStream"); } /** Construct a Multibuf_ImageInputStream that initial contains a single ByteBuffer.

@param buffer The ByteBuffer to provide stream data. @see #Add(ByteBuffer) */ public Multibuf_ImageInputStream ( ByteBuffer buffer ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Multibuf_ImageInputStream: " + buffer); Add (buffer); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Multibuf_ImageInputStream"); } /** Construct an empty Multibuf_ImageInputStream. */ public Multibuf_ImageInputStream () {} /*============================================================================== Accessors */ /** Add a ByteBuffer to the list of buffers providing stream data.

If the buffer is null or empty (zero limit) it is ignored.

A duplicate of the buffer (pointers are copied, data is not) is added to the end of the buffers list and rewound (position set to zero). The {@link #length() length} of the stream is incremented by the amount of data (limit) in the buffer. If a current buffer has not be set the new buffer is set as the current buffer.

@param buffer The ByteBuffer to be added to the buffers list. @return This Multibuf_ImageInputStream. */ synchronized public Multibuf_ImageInputStream Add ( ByteBuffer buffer ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Multibuf_ImageInputStream.Add: " + buffer); if (buffer != null && buffer.limit () != 0) { Data_Buffers.add (buffer = buffer.duplicate ()); Stream_Size += buffer.rewind ().limit (); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Buffer " + (Data_Buffers.size () - 1) + ": adds " + buffer.limit () + " of " + Stream_Size + " bytes."); if (Data_Buffer == null) Data_Buffer = buffer; } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Multibuf_ImageInputStream.Add"); return this; } /** Add the contents of another Multibuf_ImageInputStream to this Multibuf_ImageInputStream.

Each data buffer in the source stream is {@link #Add(ByteBuffer) Add}ed to the end of the list of data buffers in this stream in the order in which they are listed.

@return This Multibuf_ImageInputStream. */ synchronized public Multibuf_ImageInputStream Add ( Multibuf_ImageInputStream stream ) { for (int index = 0; index < stream.Data_Buffers.size (); index++) Add ((ByteBuffer)stream.Data_Buffers.get (index)); return this; } /** Test if the specified buffer is contained in the buffers list.

Warning: The {@link ByteBuffer#equals(Object)} method is used to compare the specified buffer against the contents of the buffers list. This compares the structure and contents of the buffers, not their object reference values. This can be expensive for large and/or many buffers. It will also test true if a buffer in the list happens to have the same content as the specified buffer even if the buffer objects are different.

@param buffer The ByteBuffer to test for. @return true if the specified buffer equals some other */ public boolean Contains ( ByteBuffer buffer ) {return Data_Buffers.contains (buffer);} /** Remove a buffer from the buffers list.

The indexed buffer is removed from the buffers list and the {@link #length() length} of the stream decremented by the amount of data (limit) in the buffer.

If the index of the removed buffer is less than the index of the current buffer the current {@link ImageInputStreamImpl#getStreamPosition() stream position} and current buffer location are decremented by the amount of data in the removed buffer.

If the index of the removed buffer is equal to the index of the current buffer the current {@link ImageInputStreamImpl#getStreamPosition() stream position} is set to the current buffer location. If there is another buffer in the buffers list following the removed buffer, the next buffer is set as the current buffer; thus the fist byte of the next available buffer will be the next byte read (unless the stream position is {@link #seek(long) moved}). However, if there is no next buffer - the removed buffer is at the end of the buffers list - but there is a buffer in the buffers list immediately preceeding the removed buffer, then the previous buffer becomes the current buffer and its position is set to its end (limit); thus the stream position is now st the end of the stream. Finally, if the removed buffer is the last one in the buffers list the current buffer is set to null and all pointers are at zero which is the end of the (now empty) stream.

@param index The index of the buffer to be removed. @return This Multibuf_ImageInputStream. @throws ArrayIndexOutOfBoundsException If the specified index is less than zero or greater than or equal to the size of the buffers list. */ synchronized public Multibuf_ImageInputStream Remove ( int index ) throws ArrayIndexOutOfBoundsException { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Multibuf_ImageInputStream.Remove"); if (index >= Data_Buffers.size () || index < 0) throw new ArrayIndexOutOfBoundsException (ID + '\n' + "Can't remove buffer at index " + index + '/' + Data_Buffers.size () + '.'); ByteBuffer buffer = (ByteBuffer)Data_Buffers.remove (index); int size = buffer.limit (); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Buffer " + index + ": removes " + size + " of " + Stream_Size + " bytes.\n" +" Buffer_Index = " + Buffer_Index + '/' + Data_Buffers.size () + '\n' +" Buffer_Location = " + Buffer_Location + '\n' +" streamPos = " + streamPos); Stream_Size -= size; if (index < Buffer_Index) { /* All locations above the removed buffer shift down by the amount of removed data. Locations remain attached to the same data. */ streamPos -= size; Buffer_Location -= size; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Buffer_Location = " + Buffer_Location + '\n' +" streamPos = " + streamPos); } else if (index == Buffer_Index) { /* The rug's been removed! Locations within the range of the removed buffer are all clamped to the location of the next datum at the beginning of the next buffer which has been moved down to the Buffer_Location. */ streamPos = Buffer_Location; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" streamPos = " + streamPos); if (Buffer_Index <= Data_Buffers.size ()) { // Use the next buffer, at the current Buffer_Index. Data_Buffer = (ByteBuffer)Data_Buffers.get (Buffer_Index); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Using next buffer: " + Data_Buffer); } else { // There is no next buffer. if (Previous_Buffer ()) { // Position at EOF which is Stream_Size. Data_Buffer.position (Data_Buffer.limit ()); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Buffer_Location = " + Buffer_Location + '\n' +" Using previous buffer: " + Data_Buffer); } else { /* There is no previous buffer; no buffers remain. At this point the stream is empty and all locations are at EOF which is zero. */ Data_Buffer = null; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" No buffers reamin."); } } } /* All locations below the removed buffer remain unaffected. */ if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Multibuf_ImageInputStream.Remove"); return this; } /** Remove the specified buffer from the buffers list.

Warning: The specified buffer is sought using the {@link List#indexOf(Object)} method on the list of data buffers which employs the {@link ByteBuffer#equals(Object)} method. See the {@link #Contains(ByteBuffer) Contains} method for details.

@param buffer The ByteBuffer to be removed. @return This Multibuf_ImageInputStream. @see #Remove(int) */ public Multibuf_ImageInputStream Remove ( ByteBuffer buffer ) { int index = Data_Buffers.indexOf (buffer); if (index >= 0) Remove (index); return this; } /** Get the List of data buffers backing this object.

Caution: Modifying the contents of the list by reordering or adding to the List, or changing the amount of valid data in a buffer (its {@link ByteBuffer#limit(int) limit}) could invalidate the current {@link ImageInputStream#getStreamPosition() stream position} and/or {@link #length() stream length} information with indeterminate (i.e. bad) consequences. Use the {@link #Add(ByteBuffer) Add} and {@link #Remove(int) Remove} methods to safely manipulate the data buffer List.

@return The List of ByteBuffer objects backing this Multibuf_ImageInputStream. */ public List Data_Buffers () {return Data_Buffers;} /*============================================================================== Manipulators */ /** Set the next buffer as the current buffer.

A next buffer is present if the current buffer index plus one is less than the size of the buffers list. In this case the Buffer_Location is incremented by the amount of data (limit) in the current buffer, the buffer index is incremented and the correspoding buffer from the buffer list is set as the current Data_Buffer. It's position is rewound to zero.

@return true if a next buffer has been set; false if there is no next buffer. */ protected boolean Next_Buffer () { if ((Buffer_Index + 1) >= Data_Buffers.size ()) // Thre is no next buffer. return false; // Move the Buffer_Location to the beginning of the next buffer. Buffer_Location += Data_Buffer.limit (); // Get the next buffer. Data_Buffer = (ByteBuffer)Data_Buffers.get (++Buffer_Index); // Expect sequential reads; seeks will repostion. Data_Buffer.rewind (); return true; } /** Set the previous data buffer as the current buffer.

If the current buffer index in the buffers list is not zero, the buffer index is decremented and the corresponding buffer from the buffer list is set as the current Data_Buffer. It's position remains unchanged. The Buffer_Location is decremented by the amount of data (limit) in the new buffer.

@return true if a previous buffer has been set; false if there is no previous buffer. */ protected boolean Previous_Buffer () { if (Buffer_Index == 0) // There is no previous buffer. return false; // Get the previous buffer. Data_Buffer = (ByteBuffer)Data_Buffers.get (--Buffer_Index); Buffer_Location -= Data_Buffer.limit (); return true; } /*============================================================================== ImageInputStream */ // Required method. /** Read a source byte.

The {@link #getBitOffset() bit offset} is set to zero.

@return The next byte value from the source. This will be -1 if the current source location is the end of the stream. */ public int read () { int datum = -1; if (streamPos < Stream_Size) { if (Data_Buffer.remaining () > 0 || Next_Buffer ()) { // Required ImageInputStream functionality. bitOffset = 0; ++streamPos; datum = (int)Data_Buffer.get () & 0xFF; } } return datum; } // Required method. /** Read a sequence of source bytes and store them in an array.

The number of bytes actually read may be less than the specified length. The length will be reduced to the smaller of the amount of space in the data array following the offset or the amount of buffered data remaining after an attempt to provide length bytes.

The {@link #getBitOffset() bit offset} is set to zero.

The current source input location is advanced by the amount of data read.

@param data The byte array into which the data is to be stored. @param offset The offset of the array where the first byte read is to be stored. @param length The number of bytes to read. @return The number of bytes read and stored. This will be -1 if the current source location is the end of the stream. @throws NullPointerException If the data array is null. @throws IndexOutOfBoundsException If the offset or length are negative, or the offset is greater than the length of the data array. */ public int read ( byte[] data, int offset, int length ) { if (streamPos >= Stream_Size) return -1; if (data == null) throw new NullPointerException (ID + '\n' + "Attempt to read into a null data array."); if (length < 0 || offset < 0 || offset >= data.length) throw new IndexOutOfBoundsException (ID + '\n' + "Attempt to read into array of length " + data.length + " at offset " + offset + " for amount " + length + '.'); if ((offset + length) > data.length) length = data.length - offset; int amount = 0; while (length > 0) { int count = Math.min (length, Data_Buffer.remaining ()); Data_Buffer.get (data, offset, count); amount += count; if ((length -= count) == 0 || ! Next_Buffer ()) break; offset += count; } // Required ImageInputStream functionality. bitOffset = 0; streamPos += amount; return amount; } /** Set the current source stream location where the next byte will be read.

The {@link #getBitOffset() bit offset} is set to zero.

@param location The source stream offset for the next read position. A location less than zero will be set to zero; a location greater than the source stream size will be set to the end of the source stream. */ public void seek ( long location ) { if (location < Stream_Size) { if (location < 0) location = 0; // Inter-buffer positioning. if (location > (Buffer_Location + Data_Buffer.limit ())) while (Next_Buffer () && location > (Buffer_Location + Data_Buffer.limit ())); else if (location < Buffer_Location) while (Previous_Buffer () && location < Buffer_Location); // Intra-buffer positioning. Data_Buffer.position ((int)(location - Buffer_Location)); streamPos = location; } else streamPos = Stream_Size; bitOffset = 0; } /** Determine if the source data is cached.

@return true */ public boolean isCached () {return true;} /** Determine if the source data is cached in memory.

@return true */ public boolean isCachedMemory () {return true;} /** Get the total size of all the buffer contents.

@return The size of the source stream. */ public long length () {return Stream_Size;} /** Suggests that data before a source location may be discared.

N.B.: No buffered data is ever discared; the suggestion is ignored.

@param location The source stream offset before which buffered data may be discarded. */ public void flushBefore ( long location ) {} /** Pretends to close the stream.

Nothing is done since the source data is externally owned. */ public void close () {} } pirl-2.3.8/PIRL/Image_Tools/JP2_Info.java0000644000175000017500000017207212052274645017545 0ustar mathieumathieu/* JP2_Info HiRISE CVS ID: JP2_Info.java,v 1.39 2012/11/19 00:13:25 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import java.io.File; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.EOFException; import javax.imageio.ImageIO; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import java.nio.ByteOrder; import java.util.Hashtable; import java.net.UnknownHostException; import PIRL.PVL.*; import PIRL.Strings.String_Buffer; // Main dependencies. import PIRL.Viewers.Parameter_View; import java.util.Vector; import java.util.Iterator; /** JP2_Info provides a set of PVL Parameters that describe the contents of a JP2 file.

The JP2 file format is defined in Part 1 of the JPEG2000 International Standard (ISO/IEC 15444-1:2004(E); "Information technology - JPEG 2000 image coding system: Core coding system"). This file format is composed entirely of a contiguous sequence of boxes. Each box has a type and length. Boxes may be nested within other boxes.

@author Bradford Castalia UA/PIRL @version 1.39 @see PIRL.Image_Tools.JPEG2000_Info @see PIRL.Image_Tools.JPEG2000_Codestream_Info */ public class JP2_Info extends JPEG2000_Info { public static final String ID = "PIRL.Image_Tools.JP2_Info (1.39 2012/11/19 00:13:25)"; //! Box Type codes. public static final int SIGNATURE_TYPE = 0x6A502020, FILE_TYPE = 0x66747970, HEADER_TYPE = 0x6A703268, IMAGE_HEADER_TYPE = 0x69686472, BITS_PER_COMPONENT_TYPE = 0x62706363, COLOUR_SPECIFICATION_TYPE = 0x636F6C72, PALETTE_TYPE = 0x70636C72, COMPONENT_MAPPING_TYPE = 0x636D6170, CHANNEL_DEFINITION_TYPE = 0x63646566, RESOLUTION_TYPE = 0x72657320, CAPTURE_RESOLUTION_TYPE = 0x72657363, DEFAULT_DISPLAY_RESOLUTION_TYPE = 0x72657364, CONTIGUOUS_CODESTREAM_TYPE = 0x6A703263, INTELLECTUAL_PROPERTY_TYPE = 0x6A703269, XML_TYPE = 0x786D6C20, UUID_TYPE = 0x75756964, UUID_INFO_TYPE = 0x75696E66, UUID_LIST_TYPE = 0x756C7374, URL_TYPE = 0x75726C20, ASSOCIATION_TYPE = 0x61736F63, LABEL_TYPE = 0X6C626C20, PLACEHOLDER_TYPE = 0x70686C64; //! Box names. public static final String SIGNATURE_NAME = "Signature", FILE_NAME = "File_Type", HEADER_NAME = "JP2_Header", IMAGE_HEADER_NAME = "Image_Header", BITS_PER_COMPONENT_NAME = "Bits_Per_Component", COLOUR_SPECIFICATION_NAME = "Colour_Specification", PALETTE_NAME = "Palette", COMPONENT_MAPPING_NAME = "Component_Mapping", CHANNEL_DEFINITION_NAME = "Channel_Definition", RESOLUTION_NAME = "Resolution", CAPTURE_RESOLUTION_NAME = "Capture_Resolution", DEFAULT_DISPLAY_RESOLUTION_NAME = "Default_Display_Resolution", CONTIGUOUS_CODESTREAM_NAME = "Contiguous_Codestream", INTELLECTUAL_PROPERTY_NAME = "Intellectual_Property", XML_NAME = "XML", UUID_NAME = "UUID", UUID_INFO_NAME = "UUID_Info", UUID_LIST_NAME = "UUID_List", URL_NAME = "URL", ASSOCIATION_NAME = "Association", LABEL_NAME = "Label", PLACEHOLDER_NAME = "Placeholder"; private static Hashtable Box_Names = new Hashtable (); static { Box_Names.put (new Integer (SIGNATURE_TYPE), SIGNATURE_NAME); Box_Names.put (new Integer (FILE_TYPE), FILE_NAME); Box_Names.put (new Integer (HEADER_TYPE), HEADER_NAME); Box_Names.put (new Integer (IMAGE_HEADER_TYPE), IMAGE_HEADER_NAME); Box_Names.put (new Integer (BITS_PER_COMPONENT_TYPE), BITS_PER_COMPONENT_NAME); Box_Names.put (new Integer (COLOUR_SPECIFICATION_TYPE), COLOUR_SPECIFICATION_NAME); Box_Names.put (new Integer (PALETTE_TYPE), PALETTE_NAME); Box_Names.put (new Integer (COMPONENT_MAPPING_TYPE), COMPONENT_MAPPING_NAME); Box_Names.put (new Integer (CHANNEL_DEFINITION_TYPE), CHANNEL_DEFINITION_NAME); Box_Names.put (new Integer (RESOLUTION_TYPE), RESOLUTION_NAME); Box_Names.put (new Integer (CAPTURE_RESOLUTION_TYPE), CAPTURE_RESOLUTION_NAME); Box_Names.put (new Integer (DEFAULT_DISPLAY_RESOLUTION_TYPE), DEFAULT_DISPLAY_RESOLUTION_NAME); Box_Names.put (new Integer (CONTIGUOUS_CODESTREAM_TYPE), CONTIGUOUS_CODESTREAM_NAME); Box_Names.put (new Integer (INTELLECTUAL_PROPERTY_TYPE), INTELLECTUAL_PROPERTY_NAME); Box_Names.put (new Integer (XML_TYPE), XML_NAME); Box_Names.put (new Integer (UUID_TYPE), UUID_NAME); Box_Names.put (new Integer (UUID_INFO_TYPE), UUID_INFO_NAME); Box_Names.put (new Integer (UUID_LIST_TYPE), UUID_LIST_NAME); Box_Names.put (new Integer (URL_TYPE), URL_NAME); Box_Names.put (new Integer (ASSOCIATION_TYPE), ASSOCIATION_NAME); Box_Names.put (new Integer (LABEL_TYPE), LABEL_NAME); Box_Names.put (new Integer (PLACEHOLDER_TYPE), PLACEHOLDER_NAME); } //! Box characterization parameter names. public static final String NAME_PARAMETER = "Name", TYPE_PARAMETER = "Type"; //! Signature box parameter. public static final String SIGNATURE_PARAMETER = "Signature"; //! Signature value. public static final int SIGNATURE = 0x0D0A870A; //! File Type box parameters. public static final String BRAND_PARAMETER = "Brand", MINOR_VERSION_PARAMETER = "Minor_Version", COMPATIBILITY_LIST_PARAMETER = "Compatibility"; //! JP2 compatibility value. public static final String JP2_COMPATIBLE = "jp2 "; //! Image Header box parameters. public static final String HEIGHT_PARAMETER = "Height", WIDTH_PARAMETER = "Width", COMPRESSION_TYPE_PARAMETER = "Compression_Type", COLORSPACE_UNKNOWN_PARAMETER = "Colorspace_Unknown", INTELLECTUAL_PROPERTY_PARAMETER = "Intellectual_Property_Rights"; //! Colour Specification box parameters. public static final String SPECIFICATION_METHOD_PARAMETER = "Specification_Method", PRECEDENCE_PARAMETER = "Precedence", COLOURSPACE_APPROXIMATION_PARAMETER = "Colourspace_Approximation", ENUMERATED_COLOURSPACE_PARAMETER = "Enumerated_Colourspace"; //! Specification method values. public static final int ENUMERATED_COLOURSPACE = 1, RESTRICTED_ICC_PROFILE = 2; //! Palette box parameters. public static final String ENTRIES_PARAMETER = "Entries", COLUMNS_PARAMETER = "Columns", VALUES_PARAMETER = "Values"; //! Component Mapping and Channel Definition parameter. public static final String MAP_PARAMETER = "Map"; //! Component map indices. public static final int COMPONENT_INDEX = 0, MAP_TYPE_INDEX = 1, PALETTE_INDEX = 2; //! Map type values. public static final int DIRECT_USE = 0, PALETTE_MAPPING = 1; //! Channel Definition indices. public static final int CHANNEL_INDEX = 0, CHANNEL_TYPE_INDEX = 1, CHANNEL_ASSOCIATION_INDEX = 2; //! Channel type values. public static final int COLOUR_IMAGE_DATA = 0, OPACITY = 1, PREMULTIPLIED_OPACITY = 2; //! Channel association values. public static final int IMAGE_ASSOCIATION = 0, NO_ASSOCIATION = 0xFFFF; //! Capture and Default Resolution parameters. public static final String VERTICAL_NUMERATOR_PARAMETER = "Vertical_Numerator", VERTICAL_DENOMINATOR_PARAMETER = "Vertical_Denominator", VERTICAL_EXPONENT_PARAMETER = "Vertical_Exponent", HORIZONTAL_NUMERATOR_PARAMETER = "Horizontal_Numerator", HORIZONTAL_DENOMINATOR_PARAMETER = "Horizontal_Denominator", HORIZONTAL_EXPONENT_PARAMETER = "Horizontal_Exponent"; //! Codestream parameters. public static final String CODESTREAM_PARAMETER = "Codestream"; private JPEG2000_Codestream_Info Codestream_Info = null; //! UUID parameter. public static final String UUID_PARAMETER = "UUID"; //! UUID value count. public static final int NUMBER_OF_UUID_VALUES = 16; //! URL parameters. public static final String VERSION_PARAMETER = "Version", FLAGS_PARAMETER = "Flags", URL_PARAMETER = "URL"; //! Text parameters. public static final String TEXT_PARAMETER = "Text"; //! JPIP Placeholder parameters. public static final String ORIGINAL_BOX_PARAMETER = "Original_Box", BIN_ID_PARAMETER = "Bin_ID", EQUIVALENT_BOX_PARAMETER = "Equivalent_Box", CODESTREAM_ID_PARAMETER = "Codestream_ID", TOTAL_CODESTREAMS_PARAMETER = "Total_Codestreams", EXTENDED_BOX_LIST_PARAMETER = "Extended_Box_List"; //! Placeholder box flags value bit masks. public static final int PLACEHOLDER_FLAGS_ORIGINAL_MASK = (1 << 0), PLACEHOLDER_FLAGS_EQUIVALENT_MASK = (1 << 1), PLACEHOLDER_FLAGS_CODESTREAM_MASK = (1 << 2), PLACEHOLDER_FLAGS_MULTIPLE_CODESTREAM_MASK = (1 << 3); //------------------------------------------------------------------------------ //! JP2 file validity flags. protected int JP2_Validity = 0; //! JP2 file validity flags. public static final int SIGNATURE_FOUND = 1 << 0, FILE_TYPE_FOUND = 1 << 1, JP2_HEADER_FOUND = 1 << 2, IMAGE_HEADER_FOUND = 1 << 3, COLOUR_SPECIFICATION_FOUND = 1 << 4, CONTIGUOUS_CODESTREAM_FOUND = 1 << 5, JP2_COMPLETE = SIGNATURE_FOUND | FILE_TYPE_FOUND | JP2_HEADER_FOUND | IMAGE_HEADER_FOUND | COLOUR_SPECIFICATION_FOUND | CONTIGUOUS_CODESTREAM_FOUND; // Commonly accessed JP2 info: private long Image_Width = -1, Image_Height = -1; private int Total_Components = -1; private int[] Bits_per_Component = new int[0]; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_PARAMETERS = 1 << 2, DEBUG_HELPERS = 1 << 3, DEBUG_MAIN = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct JP2_Info for a named source.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param source The source from which to obtain the JP2_Info. @throws UnknownHostException If the source is a URL but the hostname is specifies can not be resolved. @throws IllegalArgumentException If the source is a URL but does not specify the http protocol or a source pahtname. @throws IOException If there was a problem reading the source. */ public JP2_Info ( String source ) throws IOException, UnknownHostException, IllegalArgumentException {this (source, Skip_Tiles_Default);} /** Construct JP2_Info for a named source.

@param source The source from which to obtain the JP2_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws UnknownHostException If the source is a URL but the hostname is specifies can not be resolved. @throws IllegalArgumentException If the source is a URL but does not specify the http protocol or a source pathname. @throws IOException If there was a problem reading the source. */ public JP2_Info ( String source, boolean skip_tiles ) throws IOException, UnknownHostException, IllegalArgumentException { super (source); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JP2_Info: " + source); Read (Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JP2_Info"); } /** Construct JP2_Info for a File.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param file The File from which to obtain the JP2_Info. @throws IOException If there was a problem reading the file. */ public JP2_Info ( File file ) throws IOException {this (file, Skip_Tiles_Default);} /** Construct JP2_Info for a File.

@param file The File from which to obtain the JP2_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If there was a problem reading the file. */ public JP2_Info ( File file, boolean skip_tiles ) throws IOException { super (file); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JP2_Info: " + file); Read (Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JP2_Info"); } /** Construct JP2_Info for an ImageInputStream.

The inclusion of tile segments in the Parameters is controlled by the {@link JPEG2000_Info#Skip_Tiles_Default} flag.

@param image_input_stream The ImageInputStream from which to obtain the JP2_Info. @throws IOException If there was a problem reading the file. */ public JP2_Info ( ImageInputStream image_input_stream ) throws IOException {this (image_input_stream, Skip_Tiles_Default);} /** Construct JP2_Info for an ImageInputStream.

@param image_input_stream The ImageInputStream from which to obtain the JP2_Info. @param skip_tiles true if tile segments are not to be included in the Parameters; false otherwise. @throws IOException If there was a problem reading the file. */ public JP2_Info ( ImageInputStream image_input_stream, boolean skip_tiles ) throws IOException { super (image_input_stream); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> JP2_Info: " + image_input_stream); Read (Skip_Tiles = skip_tiles); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< JP2_Info"); } /** Construct an empty JP2_Info Object.

Use one of the Source methods to assign a source file. Then use a Read method to obtain the JP2_Info Parameter set for the file.

@see JPEG2000_Info#Source(String) @see #Read(boolean) */ public JP2_Info () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< JP2_Info"); } /*============================================================================== Accessors */ /** Read all available information from the Source file.

If no Source file has been assigned, nothing is done.

The Source is read from the beginning. The Source stream will be repositioned back to its current position before the method returns.

Any information Parameters currently present are removed. Then the file is scanned for JP2 information boxes. Each box found is used to define the contents of a Parameter Group added to the base JPEG2000_Info Aggregate. The name of each Parameter Group is the JP2 box type composed of four characters (unprintable characters are represented by escape sequences). The Group always contains the following parameters:

Name
The box type name in descriptive token form.
Type
The box type code in numeric hexadecimal form.
^Position
The position of the box in the Source as a byte offset (from 0). N.B.: This parameter is not present if {@link JPEG2000_Info#Use_Data_Position(boolean) data position inclusion} is disabled.
Length
The length of the box in bytes.

The remaining Parameters in each Group depend on the box type.

When JP2 information boxes are nested, the corresponding Parameter Groups are also nested. JPEG2000 codestream (Contiguous_Codestream, jp2c) boxes contain a Codestream Parameter Group that is defined by a JPEG2000_Codestream_Info object.

N.B.: A {@link #WARNING_PARAMETER} will be placed at the end of the Parameter set and further Source file scanning halted if the previous JP2 box is invalid. The Value of the warning Parameter will be a String describing the problem. Possible sources of warning parameters are: The beginning of the Source file does not contain a valid Signature box or the second box of the Source file is not a valid File Type box.

@param skip_tiles true if tile segments are not to be included in the Codestream Parameters; false otherwise. @return This JP2_Info. @throws IOException If there was a problem reading the Source file. @see JPEG2000_Codestream_Info */ public JP2_Info Read ( boolean skip_tiles ) throws IOException { if (Image_Input_Stream == null) return this; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> JP2_Info.Read"); Image_Input_Stream.mark (); Stream_Position (0); ByteOrder byte_order = Image_Input_Stream.getByteOrder (); if (byte_order != ByteOrder.BIG_ENDIAN) Image_Input_Stream.setByteOrder (ByteOrder.BIG_ENDIAN); // Remove any and all current parameters. Remove_All (); // Reset all image characterization values. Reset_Image_Values (); // Add the source file length. long length = -1; try {length = Image_Input_Stream.length ();} catch (IOException exception) {} if (length >= 0) { try { Add (new Parameter (DATA_LENGTH_PARAMETER) .Value (new Value (length).Units ("bytes")) .Comments ("\nTotal source file length.")); } catch (PVL_Exception exception) {} } boolean tile_skipping = Skip_Tiles; Skip_Tiles = skip_tiles; try {Add_Boxes (this, 0);} catch (IOException exception) { throw IO_Error ("Unable to Read JP2 Box information from the Source" + " at position " + Stream_Position, exception); } catch (IllegalStateException exception) { try {Add (new Parameter (WARNING_PARAMETER) .Value (exception.getMessage ()));} catch (PVL_Exception except) {} } finally { Image_Input_Stream.setByteOrder (byte_order); Skip_Tiles = tile_skipping; try {Image_Input_Stream.reset ();} catch (IOException exception) { throw IO_Error ("Could not reset the original Source position.", exception); } } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JP2_Info.Read"); return this; } /** Test if the Source is a file in JP2 format.

If the {@link #JP2_Validity() JP2 Validity} value has already been determined by {@link #Read(boolean) read}ing the Source file, then if both the {@link #SIGNATURE_FOUND} and {@link #FILE_TYPE_FOUND} validity flags have been set the file is in JP2 format. Otherwise an attempt is made to read the first two JP2 boxes from the beginning of the file which will provide the minimally necessary validity flags. In this case the Source is repositioned back to its previous position after the attempt to read the boxes. If no Source as been assigned then the return value is false.

@return true if the beginning of the Source file contains a valid signature box followed immediately by a valid file type box; false otherwise. */ public boolean Is_JP2 () { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> JP2_Info.Is_JP2"); boolean is_JP2 = false; if (Image_Input_Stream != null) { if (JP2_Validity == 0) { Image_Input_Stream.mark (); ByteOrder byte_order = Image_Input_Stream.getByteOrder (); if (byte_order != ByteOrder.BIG_ENDIAN) Image_Input_Stream.setByteOrder (ByteOrder.BIG_ENDIAN); try { Image_Input_Stream.seek (0); Stream_Position = 0; // Signature. Box_Parameter (); // File Type. Box_Parameter (); } catch (Exception exception) {} finally { Image_Input_Stream.setByteOrder (byte_order); try {Image_Input_Stream.reset ();} catch (IOException exception) {} } } is_JP2 = (JP2_Validity & (SIGNATURE_FOUND | FILE_TYPE_FOUND)) != 0; } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< JP2_Info.Is_JP2 : " + is_JP2); return is_JP2; } /** Test if the Source file contains a complete JP2 format.

The Source must be {@link #Read(boolean) read} for this test to be valid.

@return true if the {@link #JP2_Validity() JP2 Validity} value has all the {@link #JP2_COMPLETE} flags set; false otherwise. */ public boolean Is_Complete_JP2 () {return (JP2_Validity & JP2_COMPLETE) == JP2_COMPLETE;} /** Get the current JP2 validity flags.

The bit flags in this value are updated as the Source file is {@link #Read(boolean) read}:

{@link #SIGNATURE_FOUND}
Set if the beginning of the file contains a Signature box.
{@link #FILE_TYPE_FOUND}
Set if the second box of the file is a File Type box.
{@link #JP2_HEADER_FOUND}
Set if the required JP2 Header box has been found.
{@link #IMAGE_HEADER_FOUND}
Set if the required Image Header box has been found.
{@link #COLOUR_SPECIFICATION_FOUND}
Set if the required Colour Specification box has been found.
{@link #CONTIGUOUS_CODESTREAM_FOUND}
Set if a JPEG2000 Contiguous Codestream box has been found.

@return The JP2 validity flags value. */ public int JP2_Validity () {return JP2_Validity;} /** Get the number of bits per pixel for each component in the image.

@return An int array containing the number of bits per pixel for each image component, as determined from the Image Header box. A negative bits per pixel value signals that the pixel values are signed; the number of bits per pixel is abs (value). The array will be empty if the values are not yet known. */ public int[] Bits_per_Component () {return Bits_per_Component;} //! Reset all image characterization class data members. private void Reset_Image_Values () { JP2_Validity = 0; Image_Width = Image_Height = -1; Total_Components = -1; Bits_per_Component = new int[0]; } /*------------------------------------------------------------------------------ Codestream values */ /** Get the {@link JPEG2000_Codestream_Info#Codestream_Validity()} value.

@return The JPEG2000_Codestream_Info.Codestream_Validity value, or 0 if the value is not yet known. */ public int Codestream_Validity () {return (Codestream_Info == null) ? 0 : Codestream_Info.Codestream_Validity ();} /** Get the number of components (bands) in the image.

If the value has not been obtained from the Image Header box the value will be obtained from a {@link JPEG2000_Codestream_Info#Total_Components() JPEG2000 codestream} (Contiguous_Codestream, jp2c) box if available.

@return The number of image components, as determined from the Image Header box. This will be -1 if the value is not yet known. */ public int Total_Components () { if (Total_Components < 0 && Codestream_Info != null) return Codestream_Info.Total_Components (); return Total_Components; } /** Get the width of the image.

The image width is the distance on the reference grid from the {@link JPEG2000_Codestream_Info#Horizontal_Image_Offset() horizontal image offset} to the {@link JPEG2000_Codestream_Info#Reference_Grid_Width() reference grid width}. If the value has not been obtained from the Image Header box the value will be obtained from a {@link JPEG2000_Codestream_Info#Image_Width() JPEG2000 codestream} (Contiguous_Codestream, jp2c) box if available.

N.B.: The image width is measured in terms of reference grid columns. However, an individual image pixel may occupy more than one reference grid column. The {@link JPEG2000_Codestream_Info#Pixel_Width() pixel width} may be different for each image component.

@return The reference grid width of the image. This will be -1 if the value is not yet known. */ public long Image_Width () { if (Image_Width < 0 && Codestream_Info != null) return Codestream_Info.Image_Width (); return Image_Width; } /** Get the height of the image.

The image height is the distance on the reference grid from the {@link JPEG2000_Codestream_Info#Vertical_Image_Offset() vertical image offset} to the {@link JPEG2000_Codestream_Info#Reference_Grid_Height() reference grid height}. If the value has not been obtained from the Image Header box the value will be obtained from a {@link JPEG2000_Codestream_Info#Image_Height() JPEG2000 codestream} (Contiguous_Codestream, jp2c) box if available.

N.B.: The image height is measured in terms of reference grid rows. However, an individual image pixel may occupy more than one reference grid row. The {@link JPEG2000_Codestream_Info#Pixel_Height() pixel height} may be different for each image component.

@return The reference grid height of the image. This will be -1 if the value is not yet known. */ public long Image_Height () { if (Image_Height < 0 && Codestream_Info != null) return Codestream_Info.Image_Height (); return Image_Height; } /** Get the {@link JPEG2000_Codestream_Info#Reference_Grid_Width()} value.

@return The JPEG2000_Codestream_Info.Reference_Grid_Width value, or -1 if the value is not yet known. */ public long Reference_Grid_Width () {return (Codestream_Info == null) ? -1 : Codestream_Info.Reference_Grid_Width ();} /** Get the {@link JPEG2000_Codestream_Info#Reference_Grid_Height()} value.

@return The JPEG2000_Codestream_Info.Reference_Grid_Height value, or -1 if the value is not yet known. */ public long Reference_Grid_Height () {return (Codestream_Info == null) ? -1 : Codestream_Info.Reference_Grid_Height ();} /** Get the {@link JPEG2000_Codestream_Info#Horizontal_Image_Offset()} value.

@return The JPEG2000_Codestream_Info.Horizontal_Image_Offset value, or -1 if the value is not yet known. */ public long Horizontal_Image_Offset () {return (Codestream_Info == null) ? -1 : Codestream_Info.Horizontal_Image_Offset ();} /** Get the {@link JPEG2000_Codestream_Info#Vertical_Image_Offset()} value.

@return The JPEG2000_Codestream_Info.Vertical_Image_Offset value, or -1 if the value is not yet known. */ public long Vertical_Image_Offset () {return (Codestream_Info == null) ? -1 : Codestream_Info.Vertical_Image_Offset ();} /** Get the {@link JPEG2000_Codestream_Info#Pixel_Width()} value.

@return The JPEG2000_Codestream_Info.Pixel_Width value, or an empty int array if the value is not yet known. */ public int[] Pixel_Width () {return (Codestream_Info == null) ? new int[0] : Codestream_Info.Pixel_Width ();} /** Get the {@link JPEG2000_Codestream_Info#Pixel_Height()} value.

@return The JPEG2000_Codestream_Info.Pixel_Height value, or an empty int array if the value is not yet known. */ public int[] Pixel_Height () {return (Codestream_Info == null) ? new int[0] : Codestream_Info.Pixel_Height ();} /** Get the {@link JPEG2000_Codestream_Info#Tile_Width()} value.

@return The JPEG2000_Codestream_Info.Tile_Width value, or -1 if the value is not yet known. */ public long Tile_Width () {return (Codestream_Info == null) ? -1 : Codestream_Info.Tile_Width ();} /** Get the {@link JPEG2000_Codestream_Info#Tile_Height()} value.

@return The JPEG2000_Codestream_Info.Tile_Height value, or -1 if the value is not yet known. */ public long Tile_Height () {return (Codestream_Info == null) ? -1 : Codestream_Info.Tile_Height ();} /** Get the {@link JPEG2000_Codestream_Info#Horizontal_Tile_Offset()} value.

@return The JPEG2000_Codestream_Info.Horizontal_Tile_Offset value, or -1 if the value is not yet known. */ public long Horizontal_Tile_Offset () {return (Codestream_Info == null) ? -1 : Codestream_Info.Horizontal_Tile_Offset ();} /** Get the {@link JPEG2000_Codestream_Info#Vertical_Tile_Offset()} value.

@return The JPEG2000_Codestream_Info.Vertical_Tile_Offset value, or -1 if the value is not yet known. */ public long Vertical_Tile_Offset () {return (Codestream_Info == null) ? -1 : Codestream_Info.Vertical_Tile_Offset ();} /** Get the {@link JPEG2000_Codestream_Info#Total_Tiles()} value.

@return The JPEG2000_Codestream_Info.Total_Tiles value, or -1 if the value is not yet known. */ public int Total_Tiles () {return (Codestream_Info == null) ? -1 : Codestream_Info.Total_Tiles ();} /** Test if a {@link JPEG2000_Codestream_Info#Zero_Length_Tile_Part() zero length tile-part} was found in the codestream.

@return true if a zero length tile-part was found; false otherwise. */ public boolean Zero_Length_Tile_Part () {return (Codestream_Info == null) ? false : Codestream_Info.Zero_Length_Tile_Part ();} /** Get the {@link JPEG2000_Codestream_Info#Total_Quality_Layers()} value.

@return The JPEG2000_Codestream_Info.Total_Quality_Layers value, or -1 if the value is not yet known. */ public int Total_Quality_Layers () {return (Codestream_Info == null) ? -1 : Codestream_Info.Total_Quality_Layers ();} /** Get the {@link JPEG2000_Codestream_Info#Progression_Order()} value.

@return The JPEG2000_Codestream_Info.Progression_Order value, or -1 if the value is not yet known. */ public int Progression_Order () {return (Codestream_Info == null) ? -1 : Codestream_Info.Progression_Order ();} /** Get the {@link JPEG2000_Codestream_Info#Total_Resolution_Levels()} value.

@return The JPEG2000_Codestream_Info.Total_Resolution_Levels value, or -1 if the value is not yet known. */ public int Total_Resolution_Levels () {return (Codestream_Info == null) ? -1 : Codestream_Info.Total_Resolution_Levels ();} /** Get the {@link JPEG2000_Codestream_Info#Transform()} value.

@return The JPEG2000_Codestream_Info.Transform value, or -1 if the value is not yet known. */ public int Transform () {return (Codestream_Info == null) ? -1 : Codestream_Info.Transform ();} /*============================================================================== Box Parameters */ /** Assemble a Parameter Aggregate describing a JP2 box from a byte array.

A single JP2 box is parsed from the byte data. If the data contains additional JP2 box contents they are ignored. {@link JPEG2000_Info#Use_Data_Position(boolean) Including data position} parameters is disabled as is {@link JPEG2000_Info#Throw_Illegal_State(boolean) illegal box organizaion detection}.

@param data A byte array. @param offset The offset where the JP2 box is expected to start. @param length The maximum amount of byte data to be read. However, if the data array ends before length bytes after the starting offset, then only the available bytes will be read. @return A Parameter Aggregate containing Parameters describing the JP2 box contents. This will be null if the contents of a recognized box were not available within the data length. */ public static Parameter Box ( byte[] data, int offset, int length ) { if (data == null) return null; JP2_Info info = new JP2_Info (); info.Source (new MemoryCacheImageInputStream (new ByteArrayInputStream (data, offset, length))); info.Source ().setByteOrder (ByteOrder.BIG_ENDIAN); info.Use_Data_Position (false); info.Throw_Illegal_State (false); Parameter box = null; try {box = info.Box_Parameter ();} catch (Exception exception) {} return box; } /** Assemble a Parameter Aggregate from a byte array of JP2 box data.

@param data A byte array. @return A Parameter Aggregate containing Parameters describing the JP2 box contents. This will be null if the contents of a recognized box were not available within the data array. @see #Box(byte[], int, int) */ public static Parameter Box ( byte[] data ) {return Box (data, 0, data.length);} private void Add_Boxes ( Parameter container, long end_position ) throws IOException, IllegalStateException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JP2_Info.Add_Boxes: To " + container.Name () + '\n' +" From stream position " + Stream_Position + '\n' +" Until stream position " + end_position); Parameter box = null; if (end_position == 0) end_position = Long.MAX_VALUE; try { while (Stream_Position < end_position && (box = Box_Parameter ()) != null) { try {container.Add (box);} catch (PVL_Exception exception) {} } } catch (EOFException exception) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" JP2_Info.Add_Boxes: EOF at Stream_Postion " + Stream_Position); } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JP2_Info.Add_Boxes"); } private Parameter Box_Parameter () throws IOException, IllegalStateException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JP2_Info.Box_Parameter"); long box_position = Stream_Position, box_length = Read_Unsigned_int (LENGTH_PARAMETER); int type_code = Read_int (TYPE_PARAMETER); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" JP2_Info.Box_Parameter: \"" + int_to_String (type_code) + "\" (0x" + Integer.toHexString (type_code) + ") - " + Box_Name (type_code) + '\n' +" Position = " + box_position + '\n' +" Length = " + box_length); if (box_length == 0) { try { long stream_length = Image_Input_Stream.length (); box_length = stream_length - box_position; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Stream length = " + stream_length + '\n' +" Remaining length = " + box_length); } catch (IOException exception) {} } else if (box_length == 1) { box_length = Read_long ("box long length"); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Long length = " + box_length); } Parameter box = new Parameter (int_to_String (type_code)); try { box .Add (new Parameter (NAME_PARAMETER) .Value (Box_Name (type_code))) .Add (new Parameter (TYPE_PARAMETER) .Value (new Value (type_code).Base (16))); if (Use_Data_Position) box .Add (new Parameter (POSITION_PARAMETER) .Value (new Value (box_position).Units ("byte offset"))); box .Add (new Parameter (LENGTH_PARAMETER) .Value (new Value (box_length).Units ("bytes"))); } catch (PVL_Exception exception) {} if (JP2_Validity == 0) { // The first box must be the Signature. if (type_code != SIGNATURE_TYPE || box_length != 12) { if (Throw_Illegal_State) throw new IllegalStateException (ID + '\n' + "Not a valid JP2 file: No initial signature box."); } else JP2_Validity = SIGNATURE_FOUND; } else if (JP2_Validity == SIGNATURE_FOUND) { // The second box must be the File Type. if (type_code != FILE_TYPE) { if (Throw_Illegal_State) throw new IllegalStateException (ID + '\n' + "Not a valid JP2 file: Missing File Type box."); } else JP2_Validity |= FILE_TYPE_FOUND; } long end_position = box_position + box_length; switch (type_code) { // Super boxes that contain additional boxes. case HEADER_TYPE: JP2_Validity |= JP2_HEADER_FOUND; case RESOLUTION_TYPE: case UUID_INFO_TYPE: case ASSOCIATION_TYPE: Add_Boxes (box, end_position); break; // Basic boxes. case SIGNATURE_TYPE: Signature_Parameters (box); break; case FILE_TYPE: File_Type_Parameters (box, end_position); break; case IMAGE_HEADER_TYPE: JP2_Validity |= IMAGE_HEADER_FOUND; Image_Header_Parameters (box); break; case BITS_PER_COMPONENT_TYPE: Bits_per_Component_Parameters (box, end_position); break; case COLOUR_SPECIFICATION_TYPE: JP2_Validity |= COLOUR_SPECIFICATION_FOUND; Colour_Specification_Parameters (box, end_position); break; case PALETTE_TYPE: Palette_Parameters (box, end_position); break; case COMPONENT_MAPPING_TYPE: Component_Mapping_Parameters (box, end_position); break; case CHANNEL_DEFINITION_TYPE: Channel_Definition_Parameters (box); break; case CAPTURE_RESOLUTION_TYPE: case DEFAULT_DISPLAY_RESOLUTION_TYPE: Resolution_Parameters (box); break; case UUID_TYPE: UUID_Parameters (box, end_position); break; case UUID_LIST_TYPE: UUID_List_Parameters (box); break; case URL_TYPE: URL_Parameters (box, end_position); break; case LABEL_TYPE: case XML_TYPE: Text_Parameter (box, end_position); break; // JPEG2000 codestream box. case CONTIGUOUS_CODESTREAM_TYPE: JP2_Validity |= CONTIGUOUS_CODESTREAM_FOUND; Contiguous_Codestream_Parameters (box, end_position); break; // JPIP placeholder box. case PLACEHOLDER_TYPE: Placeholder_Parameters (box, end_position); break; // Opaque boxes. case INTELLECTUAL_PROPERTY_TYPE: default: if (Use_Data_Position) Data_Position_Parameters (box, end_position); else Data_Offset_Parameters (box, 0, end_position - Stream_Position); } if (Stream_Position < end_position) Stream_Position (end_position); else if (Throw_Illegal_State && Stream_Position > end_position) throw new IllegalStateException (ID + '\n' + "Invalid " + box.Name () + " contents -\n" + " expected length " + box_length + " but used length " + (Stream_Position - box_position)); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (((box == null) ? "" : box.Description ()) + '\n' +"<<< JP2_Info.Box_Parameter: " + box); return box; } private void Signature_Parameters ( Parameter box ) throws IOException, IllegalStateException { try { int signature = Read_int (SIGNATURE_PARAMETER); box.Add (new Parameter (SIGNATURE_PARAMETER) .Value (new Value (signature).Base (16))); if (Throw_Illegal_State && signature != SIGNATURE) throw new IllegalStateException (ID + '\n' + "Invalid Signature: Expected " + SIGNATURE + ", found " + signature); } catch (PVL_Exception exception) {} } private void File_Type_Parameters ( Parameter box, long end_position ) throws IOException, IllegalStateException { try { boolean JP2_compatible = false; String compatibility; int datum = Read_int (BRAND_PARAMETER); compatibility = int_to_String (datum); if (compatibility.equals (JP2_COMPATIBLE)) JP2_compatible = true; box.Add (new Parameter (BRAND_PARAMETER) .Value (compatibility)); box.Add (new Parameter (MINOR_VERSION_PARAMETER) .Value (Read_Unsigned_int (MINOR_VERSION_PARAMETER))); Value list = new Value ().Type (Value.SET); while (Stream_Position < end_position) { datum = Read_int (COMPATIBILITY_LIST_PARAMETER); compatibility = int_to_String (datum); if (compatibility.equals (JP2_COMPATIBLE)) JP2_compatible = true; list.Add (new Value (compatibility)); } box.Add (new Parameter (COMPATIBILITY_LIST_PARAMETER) .Value (list)); if (Throw_Illegal_State && ! JP2_compatible) throw new IllegalStateException (ID + '\n' + "File Type Compatibility \"" + JP2_COMPATIBLE + "\" not found."); } catch (PVL_Exception exception) {} } private void Image_Header_Parameters ( Parameter box ) throws IOException { try { Parameter value_bits; box .Add (new Parameter (HEIGHT_PARAMETER) .Value (new Value (Image_Height = Read_Unsigned_int (HEIGHT_PARAMETER)).Units ("rows"))) .Add (new Parameter (WIDTH_PARAMETER) .Value (new Value (Image_Width = Read_Unsigned_int (WIDTH_PARAMETER)).Units ("columns"))) .Add (new Parameter (TOTAL_COMPONENTS_PARAMETER) .Value (Total_Components = Read_Unsigned_short (TOTAL_COMPONENTS_PARAMETER))) .Add (value_bits = Value_Bits_Parameter (box.Name (), Stream_Position + 1)) .Add (new Parameter (COMPRESSION_TYPE_PARAMETER) .Value (Read_Unsigned_byte (COMPRESSION_TYPE_PARAMETER))) .Add (new Parameter (COLORSPACE_UNKNOWN_PARAMETER) .Value ((Read_Unsigned_byte (COLORSPACE_UNKNOWN_PARAMETER) != 0) ? "true" : "false")) .Add (new Parameter (INTELLECTUAL_PROPERTY_PARAMETER) .Value ((Read_Unsigned_byte (INTELLECTUAL_PROPERTY_PARAMETER) != 0) ? "true" : "false")); Bits_per_Component = int_Array (value_bits); } catch (PVL_Exception exception) {} } private void Bits_per_Component_Parameters ( Parameter box, long end_position ) throws IOException { try { Parameter value_bits; box.Add (value_bits = Value_Bits_Parameter (box.Name (), end_position)); // The values from this box overrides the values from the Image Header box. Bits_per_Component = int_Array (value_bits); } catch (PVL_Exception exception) {} } private void Colour_Specification_Parameters ( Parameter box, long end_position ) throws IOException { try { long start_position = Stream_Position; int method = Read_Unsigned_byte (SPECIFICATION_METHOD_PARAMETER); box .Add (new Parameter (SPECIFICATION_METHOD_PARAMETER) .Value (method)) .Add (new Parameter (PRECEDENCE_PARAMETER) .Value (Read_Unsigned_byte (PRECEDENCE_PARAMETER))) .Add (new Parameter (COLOURSPACE_APPROXIMATION_PARAMETER) .Value (Read_Unsigned_byte (COLOURSPACE_APPROXIMATION_PARAMETER))); if (method == ENUMERATED_COLOURSPACE) box.Add (new Parameter (ENUMERATED_COLOURSPACE_PARAMETER) .Value (Read_Unsigned_int (ENUMERATED_COLOURSPACE_PARAMETER))); else { if (Use_Data_Position) Data_Position_Parameters (box, end_position); else Data_Offset_Parameters (box, Stream_Position - start_position, end_position - start_position); } } catch (PVL_Exception exception) {} } private void Palette_Parameters ( Parameter box, long end_position ) throws IOException { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> JP2_Info.Palette_Parameters:\n" +" From stream position " + Stream_Position + '\n' +" Until stream position " + end_position); try { int entries = Read_Unsigned_short (box.Name () + ' ' + ENTRIES_PARAMETER), columns = Read_Unsigned_byte (COLUMNS_PARAMETER); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" entries = " + entries + '\n' +" columns = " + columns); box.Add (new Parameter (ENTRIES_PARAMETER) .Value (entries)); box.Add (new Parameter (COLUMNS_PARAMETER) .Value (columns)); Parameter value_bits = Value_Bits_Parameter (box.Name (), Stream_Position + columns); box.Add (value_bits); int[] bits_per_column = int_Array (value_bits); if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" bits_per_column = " + bits_per_column); Value palette = new Value ().Type (Value.SEQUENCE), palette_row; while (entries-- != 0) { palette_row = new Value ().Type (Value.SEQUENCE); for (int column = 0; column < columns; ++column) palette_row.Add (new Value (Read_Value (box.Name () + ' ' + VALUES_PARAMETER, bits_per_column[column]))); palette.Add (palette_row); } box.Add (new Parameter (VALUES_PARAMETER) .Value (palette)); } catch (PVL_Exception exception) {} if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< JP2_Info.Palette_Parameters"); } private void Component_Mapping_Parameters ( Parameter box, long end_position ) throws IOException { try { String description = box.Name () + ' ' + MAP_PARAMETER; Value map = new Value ().Type (Value.SEQUENCE), set; while (Stream_Position < end_position) { set = new Value ().Type (Value.SEQUENCE) .Add (new Value (Read_Unsigned_short (description + " component index"))) .Add (new Value (Read_Unsigned_byte (description + " map type"))) .Add (new Value (Read_Unsigned_byte (description + " palette index"))); map.Add (set); } box.Add (new Parameter (MAP_PARAMETER) .Comments ("\nMap entries:\n" + " Component index,\n" + " Map type,\n" + " Palette index") .Value (map)); } catch (PVL_Exception exception) {} } private void Channel_Definition_Parameters ( Parameter box ) throws IOException { try { int entries = Read_Unsigned_short (box.Name () + ' ' + ENTRIES_PARAMETER); box.Add (new Parameter (ENTRIES_PARAMETER) .Value (entries)); String description = box.Name () + ' ' + MAP_PARAMETER; Value map = new Value ().Type (Value.SEQUENCE), set; while (entries-- != 0) { set = new Value ().Type (Value.SEQUENCE) .Add (new Value (Read_Unsigned_short (description + " channel index"))) .Add (new Value (Read_Unsigned_short (description + " channel type"))) .Add (new Value (Read_Unsigned_short (description + " channel association"))); map.Add (set); } box.Add (new Parameter (MAP_PARAMETER) .Comments ("\nChannel definition entries:\n" + " Channel index,\n" + " Channel type,\n" + " Channel association") .Value (map)); } catch (PVL_Exception exception) {} } private void Resolution_Parameters ( Parameter box ) throws IOException { try { box .Add (new Parameter (VERTICAL_NUMERATOR_PARAMETER) .Value (Read_Unsigned_short (VERTICAL_NUMERATOR_PARAMETER))) .Add (new Parameter (VERTICAL_DENOMINATOR_PARAMETER) .Value (Read_Unsigned_short (VERTICAL_DENOMINATOR_PARAMETER))); int numerator = Read_Unsigned_short (HORIZONTAL_NUMERATOR_PARAMETER), denominator = Read_Unsigned_short (HORIZONTAL_DENOMINATOR_PARAMETER); box .Add (new Parameter (VERTICAL_EXPONENT_PARAMETER) .Value (Read_byte (VERTICAL_EXPONENT_PARAMETER))) .Add (new Parameter (HORIZONTAL_NUMERATOR_PARAMETER) .Value (numerator)) .Add (new Parameter (HORIZONTAL_DENOMINATOR_PARAMETER) .Value (denominator)) .Add (new Parameter (HORIZONTAL_EXPONENT_PARAMETER) .Value (Read_byte (HORIZONTAL_EXPONENT_PARAMETER))); } catch (PVL_Exception exception) {} } private void UUID_Parameters ( Parameter box, long end_position ) throws IOException { try { long start_position = Stream_Position; box.Add (UUID_Parameter (box.Name ())); if (Use_Data_Position) Data_Position_Parameters (box, end_position); else Data_Offset_Parameters (box, Stream_Position - start_position, end_position - start_position); } catch (PVL_Exception exception) {} } private void UUID_List_Parameters ( Parameter box ) throws IOException { try { int entries = Read_Unsigned_short (box.Name () + ' ' + ENTRIES_PARAMETER); box.Add (new Parameter (ENTRIES_PARAMETER) .Value (entries)); while (entries-- != 0) box.Add (UUID_Parameter (box.Name ())); } catch (PVL_Exception exception) {} } private Parameter UUID_Parameter ( String description ) throws IOException { String describe = description + ' ' + UUID_PARAMETER; Parameter parameter = new Parameter (UUID_PARAMETER); try { Value uuid = new Value ().Type (Value.SEQUENCE); for (int index = 0; index < NUMBER_OF_UUID_VALUES; ++index) uuid.Add (new Value (Read_Unsigned_byte (describe)).Base (16)); parameter.Value (uuid); } catch (PVL_Exception exception) {} return parameter; } private void URL_Parameters ( Parameter box, long end_position ) throws IOException { try { box .Add (new Parameter (VERSION_PARAMETER) .Value (Read_Unsigned_byte (box.Name () + ' ' + VERSION_PARAMETER))) .Add (new Parameter (FLAGS_PARAMETER) .Value (new Value (Read_Value (box.Name () + ' ' + FLAGS_PARAMETER, 24)) .Base (16))) .Add (new Parameter (URL_PARAMETER) .Value (Read_String (box.Name (), end_position))); } catch (PVL_Exception exception) {} } private void Text_Parameter ( Parameter box, long end_position ) throws IOException { try { box .Add (new Parameter (TEXT_PARAMETER) .Value (new Value (Read_String (box.Name (), end_position)) .Type (Value.TEXT))); } catch (PVL_Exception exception) {} } private void Contiguous_Codestream_Parameters ( Parameter box, long end_position ) throws IOException { if (Use_Data_Position) Data_Position_Parameters (box, end_position); else Data_Offset_Parameters (box, 0, end_position - Stream_Position); try { JPEG2000_Codestream_Info codestream_info = new JPEG2000_Codestream_Info (Image_Input_Stream, end_position, Skip_Tiles); codestream_info.Name (CODESTREAM_PARAMETER); box.Add (codestream_info); if (Codestream_Info == null) Codestream_Info = codestream_info; } catch (PVL_Exception exception) {} } private void Placeholder_Parameters ( Parameter box, long end_position ) throws IOException { try { long start_position = Stream_Position; int flags = Read_int (box.Name () + ' ' + FLAGS_PARAMETER); box .Add (new Parameter (FLAGS_PARAMETER) .Value (new Value (flags).Base (16)) .Comments ("\n" + (((flags & PLACEHOLDER_FLAGS_ORIGINAL_MASK) == 0) ? "No o" : "O") + "riginal box provided.\n" + (((flags & PLACEHOLDER_FLAGS_EQUIVALENT_MASK) == 0) ? "No e" : "E") + "quivalent box provided.\n" + (((flags & PLACEHOLDER_FLAGS_CODESTREAM_MASK) == 0) ? "No c" : "C") + "odestream provided.")); Parameter group; if ((flags & PLACEHOLDER_FLAGS_ORIGINAL_MASK) != 0) { group = new Parameter (ORIGINAL_BOX_PARAMETER); group.Add (new Parameter (BIN_ID_PARAMETER) .Value (Read_long (box.Name () + ' ' + ORIGINAL_BOX_PARAMETER + ' ' + BIN_ID_PARAMETER))); Box_Header_Parameters (group); box.Add (group); } if ((flags & PLACEHOLDER_FLAGS_EQUIVALENT_MASK) != 0) { group = new Parameter (EQUIVALENT_BOX_PARAMETER); group.Add (new Parameter (BIN_ID_PARAMETER) .Value (Read_long (box.Name () + ' ' + EQUIVALENT_BOX_PARAMETER + ' ' + BIN_ID_PARAMETER))); Box_Header_Parameters (group); box.Add (group); } if ((flags & PLACEHOLDER_FLAGS_CODESTREAM_MASK) != 0) { box.Add (new Parameter (CODESTREAM_ID_PARAMETER) .Value (Read_long (box.Name () + ' ' + CODESTREAM_ID_PARAMETER))); long total = Read_Unsigned_int (box.Name () + ' ' + TOTAL_CODESTREAMS_PARAMETER); if ((flags & PLACEHOLDER_FLAGS_MULTIPLE_CODESTREAM_MASK) == 0) total = 1; box.Add (new Parameter (TOTAL_CODESTREAMS_PARAMETER) .Value (total)); } if (Stream_Position != end_position) { group = new Parameter (EXTENDED_BOX_LIST_PARAMETER); if (Use_Data_Position) Data_Position_Parameters (group, end_position); else Data_Offset_Parameters (group, Stream_Position - start_position, end_position - start_position); box.Add (group); } } catch (PVL_Exception exception) {} } private void Box_Header_Parameters ( Parameter box ) throws IOException { try { long box_length = Read_Unsigned_int (box.Name () + ' ' + LENGTH_PARAMETER); int type_code = Read_int (box.Name () + ' ' + TYPE_PARAMETER); if (box_length == 1) box_length = Read_long (box.Name () + " box long length"); box .Add (new Parameter (NAME_PARAMETER) .Value (Box_Name (type_code))) .Add (new Parameter (TYPE_PARAMETER) .Value (new Value (type_code).Base (16))) .Add (new Parameter (LENGTH_PARAMETER) .Value (new Value (box_length).Units ("bytes"))); } catch (PVL_Exception exception) {} } /*============================================================================== Helpers */ /** Get the descriptive box name for a box type.

@param type_code The box type code value. This may be obtained from the {@link #TYPE_PARAMETER} present in each box Parameter Group. @return The box name String as used in the {@link #NAME_PARAMETER} of each box Parameter Group. */ public static String Box_Name ( int type_code ) { String string = (String)Box_Names.get (new Integer (type_code)); if (string == null) string = UNKNOWN; return string; } /** Convert an int value to a String.

Each byte, starting with the most significant byte, of the value is appended as a character to a String. Non-printable characters are expanded to escape sequences.

@param value The int value to be converted. @return The String repesentation of the value. */ public static String int_to_String ( int value ) { String_Buffer string = new String_Buffer (); int shift = 24, mask = 0x7F000000; for (shift = 24; shift >= 0; shift -= 8, mask >>= 8) string.append ((char)((value & mask) >> shift)); // Escape any non-printable characters. string.special_to_escape (); return string.toString (); } /** Generate an IOException containing a message.

If the provided message does not already contain the class {@link #ID} the ID and a NL character is prepended to the message (or the ID becomes the message if it is null). If a non-null exception is provided, the message from the exception is appended, after a NL character, to the message used in constructing the IOException.

If the exception argument is an instance of EOFException the returned IOException will also be an EOFException. If the exception argument is non-null and contains a cause, an attempt is made to apply the cause to the new IOException (failure to apply the cause is ignored).

@param message The message String to be included in the IOException. @return An IOException. @see #IOException(String, Exception) */ private IOException IO_Error ( String message, Exception exception ) { if (message == null) message = ID; else if (message.indexOf (ID) < 0) message = ID + '\n' + message; if (exception != null) message += "\n" + exception.getMessage (); IOException except; if (exception instanceof EOFException) except = new EOFException (message); else except = new IOException (message); if (exception != null) { Throwable cause = exception.getCause (); if (cause != null) { try {except.initCause (cause);} catch (IllegalStateException e) {} } } return except; } /** Generate an IOException containing a message.

@param message The message String to be included in the IOException. @return An IOException. @see #IOException(String, Exception) */ private IOException IO_Error ( String message ) {return IO_Error (message, null);} /*============================================================================== Main */ //! Exit status values. public static final int SUCCESS = 0, SYNTAX_ERROR = 1, IO_EXCEPTION = 2, NO_INFO = 3, PVL_ERROR = 4, INVALID_FILE = 5; /** Report the JP2_Info found in a file.

@param arguments The arguments String array. @see #Usage() */ public static void main ( String[] arguments ) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println ("*** " + ID); boolean skip_tiles = true, EOC_search = false, GUI = false; String source = null; Vector names = new Vector (), patterns = new Vector (); int exit_status = SUCCESS; for (int argument = 0; argument < arguments.length; ++argument) { if (arguments[argument].startsWith ("-") && arguments[argument].length () > 1) { switch (arguments[argument].charAt (1)) { case 'N': case 'n': int index = arguments[argument].indexOf ('_'); if (index < 0) { System.out.println ("Unknown option: " + arguments[argument]); Usage (); } switch (arguments[argument].charAt (++index)) { case 'T': case 't': skip_tiles = true; break; case 'E': case 'e': EOC_search = false; break; default: System.out.println ("Unknown option: " + arguments[argument]); } break; case 'T': case 't': skip_tiles = false; break; case 'E': case 'e': EOC_search = true; break; case 'F': case 'f': if (++argument == arguments.length) Usage (); names.add (arguments[argument]); break; case 'P': case 'p': if (++argument == arguments.length) Usage (); patterns.add (arguments[argument]); break; case 'G': case 'g': GUI = true; break; default: System.out.println ("Unknown option: " + arguments[argument]); case 'H': case 'h': Usage (); } } else if (source == null) source = arguments[argument]; else { System.err.println ("Only one source, please."); Usage (); } } if (source == null) { System.err.println ("A source must be specified."); Usage (); } // Get the Info. JP2_Info info = null; JPEG2000_Codestream_Info.EOC_Search = EOC_search; try {info = new JP2_Info (source, skip_tiles);} catch (Exception exception) { System.out.println (exception.getMessage ()); System.exit (IO_EXCEPTION); } // Report the Info. if (info == null) { System.out.println ("No JP2 Info"); System.exit (NO_INFO); } int validity; if (! info.Is_Complete_JP2 ()) { System.out.println ("\n>>> WARNING <<< Incomplete JP2 file contents!\n"); validity = info.JP2_Validity (); if ((validity & SIGNATURE_FOUND) == 0) System.out.println (" Signature box not found."); if ((validity & FILE_TYPE_FOUND) == 0) System.out.println (" File Type box not found."); if ((validity & JP2_HEADER_FOUND) == 0) System.out.println (" JP2 Header box not found."); if ((validity & IMAGE_HEADER_FOUND) == 0) System.out.println (" Image Header box not found."); if ((validity & COLOUR_SPECIFICATION_FOUND) == 0) System.out.println (" Colour Specification box not found."); if ((validity & CONTIGUOUS_CODESTREAM_FOUND) == 0) System.out.println (" JPEG2000 Contiguous Codestream box not found."); System.out.println (); exit_status = INVALID_FILE; } validity = info.Codestream_Validity (); boolean EOC_missing = ((! info.Skip_Tiles ()) && ((validity & JPEG2000_Codestream_Info.EOC_FOUND) == 0)) || (info.Zero_Length_Tile_Part () && JPEG2000_Codestream_Info.EOC_Search); if ((validity != 0 && (validity != JPEG2000_Codestream_Info.CODESTREAM_COMPLETE)) || EOC_missing) { if (info.Is_Complete_JP2 ()) System.out.println (); System.out.println (">>> WARNING <<< Incomplete JPEG2000 codestream.\n"); if ((validity & JPEG2000_Codestream_Info.SOC_FOUND) == 0) System.out.println (" Start of Codestream marker not found."); if ((validity & JPEG2000_Codestream_Info.SIZ_FOUND) == 0) System.out.println (" Size marker not found."); if ((validity & JPEG2000_Codestream_Info.COD_FOUND) == 0) System.out.println (" Coding Style Default marker not found."); if ((validity & JPEG2000_Codestream_Info.QCD_FOUND) == 0) System.out.println (" Quantization Default marker not found."); if (EOC_missing) System.out.println (" End of Codestream marker not found."); System.out.println (); exit_status = INVALID_FILE; } if (! names.isEmpty () || ! patterns.isEmpty ()) { Parameter select = new Parameter (), parameter; Selector criteria; String name; int root_name_length = info.Name ().length () + 1; Iterator list; if (! names.isEmpty ()) { criteria = new Selection ().Name (true).Specific (false); list = names.iterator (); while (list.hasNext ()) { select.Name ((String)list.next ()); parameter = null; while ((parameter = info.Find (select, criteria, parameter)) != null) { name = parameter.Name (); parameter.Name (parameter.Path_to_Name () .substring (root_name_length) + name); try {parameter.Write ();} catch (Exception exception) { System.out.println ("PVL error -\n" + exception.getMessage () + "\n"); System.exit (PVL_ERROR); } parameter.Name (name); } } } if (! patterns.isEmpty ()) { criteria = new Selection ().Name (true).Pattern_Match (true); list = patterns.iterator (); while (list.hasNext ()) { select.Name ((String)list.next ()); parameter = null; while ((parameter = info.Find (select, criteria, parameter)) != null) { name = parameter.Name (); parameter.Name (parameter.Path_to_Name () .substring (root_name_length) + name); try {parameter.Write ();} catch (Exception exception) { System.out.println ("PVL error -\n" + exception.getMessage () + "\n"); System.exit (PVL_ERROR); } parameter.Name (name); } } } } else if (GUI) { Parameter_View view = new Parameter_View (info); view.setVisible (true); } else { System.out.println (String_Buffer.escape_to_special (info.Description ())); System.exit (exit_status); } } /** Print the command line usage syntax.

Usage

JP2_Info [-[No_]Tiles] [-Gui] [-Find <parameter>] [-Pattern <regex>] <source>

Description

The contents of the specified file are scanned for JP2 file format boxes and JPEG2000 codestream segments. The information found is reported in the form of PVL parameters containing all the data element values.

Options

-[No_]Tiles

Tiles segment parameters are to be included (or not). The default is not to include tile segment parameters.

-Gui

The information PVL is provided in a GUI viewer. The default is to provide a terminal listing.

-Find <parameter>

The named parameter is listed. This option may be repeated to obtain a listing of all parameters specified.

-Pattern <regex>

The parameters with names that match the regular expression are listed. This option may also be repeated.

Exit Status

0 - Success
1 - Command line syntax problem
2 - I/O Exception while attemping to read the source file
3 - No information was found
4 - An error occurred in assembing the PVL parameters for the JP2 information
5 - The source file does not have the required JP2 contents

N.B.This method always results in a System.exit with a status of 1. */ public static void Usage () { System.out.println ( ID + '\n' + "Usage: JP2_Info [] \n" + " The contents of the specified file are scanned\n" + " for JP2 file format boxes and JPEG2000 codestream segments.\n" + " The information found is reported in the form of\n" + " PVL parameters containing all the data element values.\n" + "\n" + "Options -\n" + " -[No_]Tiles\n" + " Tiles segment parameters are to be included (or not).\n" + " The default is not to include tile segment parameters.\n" + " -[No_]EOC_search\n" + " Search a final tile-part of unknown length for the EOC marker.\n" + " The default is not to search.\n" + " -Gui\n" + " The information PVL is provided in a GUI viewer.\n" + " The default is to provide a terminal listing.\n" + " -Find \n" + " The named parameter is listed. This option may be repeated\n" + " to obtain a listing of all parameters specified.\n" + " -Pattern \n" + " The parameters with names that match the regular expression\n" + " are listed. This option may also be repeated.\n" + " -Help\n" + " Print this usage description and exit.\n" + "Exit Status -\n" + " 0 - Success\n" + " 1 - Command line syntax problem\n" + " 2 - I/O Exception while attemping to read the source file\n" + " 3 - No information was found\n" + " 4 - An error occurred in assembing the PVL parameters for the JP2 information\n" + " 5 - The source file does not have the required JP2 contents\n" ); System.exit (SYNTAX_ERROR); } } pirl-2.3.8/PIRL/Image_Tools/HTTP_ImageInputStream.java0000644000175000017500000022565211742733714022261 0ustar mathieumathieu/* HTTP_ImageInputStream PIRL CVS ID: HTTP_ImageInputStream.java,v 1.14 2012/04/16 06:10:20 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import PIRL.Strings.Words; import PIRL.Strings.String_Buffer; import javax.imageio.stream.ImageInputStreamImpl; import java.net.URL; import java.net.MalformedURLException; import java.net.UnknownHostException; import java.net.ProtocolException; import java.net.URLEncoder; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.io.BufferedWriter; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.BufferOverflowException; import java.lang.NumberFormatException; import java.lang.IllegalArgumentException; import java.util.Vector; /** An HTTP_ImageInputStream provides an ImageInputStream interface to a remote source file accessed by a URL reference.

The URL used to reference the source file must use the http protocol. Any HTTP server compatible with version 1.1 of the protocol should be capable of providing selective access to the source file.

The source file data is buffered using a sliding window that is mapped to an arbitrary location offset of the source file. Data is fetched on demand using a Content_Range request to the server from which the response data content is placed at the appropriate position in the buffer. During normal read operations when the end of buffered data is reached a relatively small input increment is fetched into the buffer. When the amount of data has reached the buffer capacity the buffer "slides" forward, dropping data from the beginning of the buffer Arbitrary random access seeks within the source file are supported. If the seek location is currently buffered, no data fetch is needed, of course. Seeks beyond the range of buffered data to a "nearby" location will retain as much buffered data as possible to optimize short seeks.

If the connection to the server is lost repeated attempts are made to reconnect. Reconnecting to the server does cause any loss of buffered data.

@author Bradford Castalia UA/PIRL @version 1.14 @see javax.imageio.stream.ImageInputStream */ public class HTTP_ImageInputStream extends ImageInputStreamImpl { public static final String ID = "PIRL.Image_Tools.HTTP_ImageInputStream (1.14 2012/04/16 06:10:20)"; //------------------------------------------------------------------------------ // Data source /** The HTTP version identifier used in communication with the server. */ public static final String HTTP_VERSION = "HTTP/1.1"; /** The character encoding for messages sent to the server. */ public static final String ENCODING_SCHEME = "UTF-8"; /** The default {@link #User_Agent(String) User Agent} reported to the server. */ public static final String DEFAULT_USER_AGENT = "HTTP_ImageInputStream 1.14 2012/04/16 06:10:20"; /** The default port to use for communication with the server.

The URL used to construct the HTTP_ImageInputStream may specify a different port to be used. */ public static final int DEFAULT_PORT = 80; /** The amount of time (ms) to wait for a server connection. */ public static final int DEFAULT_CONNECT_TIMEOUT = 5000; /** The amount of time (ms) to wait before trying to reconnect with the server. */ public static final long RECONNECT_WAIT = 3000; /** The maximum number of repeated server reconnect tries. */ public static final int MAX_RECONNECT_ATTEMPTS = 5; /** HTTP {@link #Status() Status} code when the source can not be found on the server. */ public static final int SOURCE_NOT_FOUND_STATUS = 404; /** HTTP {@link #Status() Status} code value below which the request is deemed successful. */ public static final int SUCCESS_STATUS_LIMIT = 300; private URL Source_URL = null; private static String Default_User_Agent = DEFAULT_USER_AGENT; private String User_Agent = Default_User_Agent, Server_Hostname = null, Source_Pathname = null; private InetSocketAddress Server_Address = null; private Socket Server_Socket = null; private BufferedWriter Server_Writer = null; private static final int HEADERS_BUFFER_SIZE = 512; private InputStream Server_Input = null; /** Source size value if no source has been found. */ protected long NO_SOURCE_SIZE = Long.MAX_VALUE; /** Total size (bytes) of the source data file.

The value is initialized to {@link #NO_SOURCE_SIZE} to indicate that the size is unknown. When HTTP response {@link #Headers() headers} are received an attempt is made to get the {@link #Source_Size() size of the source file} if it is not already known. */ protected long Source_Size = NO_SOURCE_SIZE; // Get the size from the server. /* !!! Workaround !!! The reset mehod of ImageInputStreamImpl throws an exception if the position popped from the markByteStack is located before the flushedPos (beginning of the buffer). This implementation allows seeking to a location before the beginning of the buffer. The workaround is to leave the flushedPos at zero and use an alternate variable for the buffer stream location. */ private long Buffer_Location = 0; //! Total number of bytes transmitted to/from the sever. private long Total_Sent = 0, Total_Received = 0; //------------------------------------------------------------------------------ // HTTP headers private Vector HTTP_Headers = new Vector (); private int HTTP_Status = 0; private boolean Logging = false; /** When {@link #Logging(boolean) Logging} is enabled, the prefix to print before each request message line that is reported sent to the server. */ public static final String LOG_OUTPUT_PREFIX = "<< "; /** When {@link #Logging(boolean) Logging} is enabled, the prefix to print before each response header line that is reported received from the server. */ public static final String LOG_INPUT_PREFIX = " >> "; private static final String WHITESPACE = " \t", HTTP_VERSION_DELIMITERS = "/" + WHITESPACE, FIELD_CONTENT_DELIMITERS = ":;,&" + WHITESPACE, VALUE_DELIMITERS = "=" + WHITESPACE, EOL = "\r\n"; //------------------------------------------------------------------------------ // DEBUG control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_SEND = 1 << 2, DEBUG_HELPERS = 1 << 3, DEBUG_HEADERS = 1 << 4, DEBUG_DATA = 1 << 5, DEBUG_RECEIVE = 1 << 6, DEBUG_CONNECT = 1 << 7, DEBUG_BUFFERS = 1 << 8, DEBUG_IIS = 1 << 9, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; // DEBUG = DEBUG_ALL & ~DEBUG_DATA & ~DEBUG_HEADERS; /*============================================================================== Constructors */ /** Constructs an HTTP_ImageInputStream on a URL.

After a successful {@link #Connect(URL) connect}ion to the server has been established, a data storage buffer is {@link #Allocate_Buffer(int) allocated} with the {@link #DEFAULT_BUFFER_SIZE} capacity. Then the validity of the source is {@link #Check_Source() checked}. If the source can not be confirmed the connection to the server is closed. Otherwise, if the server response to the source check contains a {@link #Content_Length() content length} that is used to set the size of the source file.

@param url The URL to use to access the source file. @throws IllegalArgumentException If the URL does not specify the http protocol nor provide a source file pathname. @throws UnknownHostException If the hostname of the URL can not be resolved to an internet address. @throws FileNotFoundException If the source was not found on the server. @throws ProtocolException If an HTTP {@link #Status() Status} protocol error was returned by the server. @throws IOException If the server connection could not be established. */ public HTTP_ImageInputStream ( URL url ) throws IOException, UnknownHostException, IllegalArgumentException { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) { System.out.println (">>> HTTP_ImageInputStream: URL " + url); Logging (true); } Connect (url); Allocate_Buffer (DEFAULT_BUFFER_SIZE); int source_status = Check_Source (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Source status: " + source_status); int attempt = 0; while (source_status >= SUCCESS_STATUS_LIMIT && attempt < MAX_RECONNECT_ATTEMPTS) { if (Redirect ()) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Redirected to " + Source_URL); source_status = Check_Source (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Source status: " + source_status); ++attempt; } else break; } if (source_status >= SUCCESS_STATUS_LIMIT) { close (); if (source_status == SOURCE_NOT_FOUND_STATUS) throw new FileNotFoundException (ID + '\n' + "URL not found: " + url); else if (attempt == MAX_RECONNECT_ATTEMPTS) throw new ProtocolException (ID + '\n' + "Too many server redirects for source at " + url + '\n' + "Last attempt was to URL " + Source_URL); else throw new ProtocolException (ID + '\n' + "Status " + source_status + " for source at " + url); } long length = Content_Length (); if (length >= 0) Source_Size = length; if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< HTTP_ImageInputStream: Source size = " + Source_Size); } /*============================================================================== Accessors */ /** Get the URL used to access the source.

@return The source URL. */ public URL Source_URL () {return Source_URL;} /** Get the source server hostname.

@return The hostname String for the server. */ public String Server_Hostname () {return Server_Hostname;} /** Get the source pathname.

@return The pathname String where the source to be found by the server. */ public String Source_Pathname () {return Source_Pathname;} /** Get the HTTP user agent name reported to the server.

@return The user agent name String. @see #User_Agent(String) */ public String User_Agent () {return User_Agent;} /** Set the HTTP user agent name to be reported to the server.

Every time a request is sent to the server a "User-Agent" message is included. This information can be used by the server to gather statistics on it's use by the identified client agent. When an HTTP_ImageInputStream object is constructed the user agent is set as the {@link #Default_User_Agent}.

@param name The user agent name String. If this is null, the {@link #Default_User_Agent} will be assigned. @return This HTTP_ImageInputStream. */ public HTTP_ImageInputStream User_Agent ( String name ) { if (name == null || name.length () == 0) User_Agent = Default_User_Agent; else User_Agent = name; return this; } /** Get the default user agent that will be used with new HTTP_ImageInputStream objects.

@return The default user agent String. @see #User_Agent(String) */ public static String Default_User_Agent () {return Default_User_Agent;} /** Set the default user agent that will be used with new HTTP_ImageInputStream objects.

@param name The user agent name String. If this is null, the {@link #DEFAULT_USER_AGENT} will be assigned. @see #User_Agent(String) */ public static void Default_User_Agent ( String name ) { if (name == null || name.length () == 0) Default_User_Agent = DEFAULT_USER_AGENT; else Default_User_Agent = name; } /** Get the total number of bytes sent to the server so far.

@return A long value that is the number of bytes that have been sent to the server. */ public long Total_Sent () {return Total_Sent;} /** Get the total number of bytes received from the server so far.

@return A long value that is the number of bytes that have been received from the server. */ public long Total_Received () {return Total_Received;} /** Check if server communication logging is enabled.

@return true if logging is enabled; false otherwise. @see #Logging(boolean) */ public boolean Logging () {return Logging;} /** Enable or disable server communication logging.

When logging is enabled, each message {@link #Send(String) sent} to the server is printed to stdout preceded by the {@link #LOG_OUTPUT_PREFIX} String, and each {@link #Headers() header} response line is printed preceded by the {@link #LOG_INPUT_PREFIX} String. In addition, the amount of data content {@link #Receive() receive}d is printed on a ling after a LOG_INPUT_PREFIX.

@param enabled true if logging is to be enabled; false otherwise. @return This HTTP_ImageInputStream. */ public HTTP_ImageInputStream Logging ( boolean enabled ) { Logging = enabled; return this; } /*============================================================================== ImageInputStream */ // Required method. /** Read a source byte.

The {@link #getBitOffset() bit offset} is set to zero.

@return The next byte value from the source. This will be -1 if the current source location is end of file. @throws IOException If access to the source is closed or there was a problem obtaining source data. */ public int read () throws IOException { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (">>> HTTP_ImageInputStream.read:\n" +" stream location " + Stream_Location () + " (" + streamPos + ')'); int datum = -1; if (Stream_Location () < Source_Size) { if (Image_Buffer.remaining () > 0 || Fetch (INPUT_INCREMENT) > 0) { // Required ImageInputStream functionality. bitOffset = 0; ++streamPos; datum = (int)Image_Buffer.get () & 0xFF; } } if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.read: 0x" + Integer.toHexString (datum)); return datum; } // Required method. /** Read a sequence of source bytes and store them in an array.

The number of bytes actually read may be less than the specified length. The length will be reduced to the smaller of the amount of space in the data array following the offset or the amount of buffered data remaining after an attempt to provide length bytes.

The {@link #getBitOffset() bit offset} is set to zero.

The current source input location is advanced by the amount of data read.

@param data The byte array into which the data is to be stored. @param offset The offset of the array where the first byte read is to be stored. @param length The number of bytes to read. @return The number of bytes read and stored. This will be -1 if the current source location is end of file. @throws NullPointerException If the data array is null. @throws IndexOutOfBoundsException If the offset or length are negative, or the offset is greater than the length of the data array. @throws IOException If access to the source is closed or there was a problem obtaining source data. @see #Fetch(int) */ public int read ( byte[] data, int offset, int length ) throws IOException { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (">>> HTTP_ImageInputStream.read: data" + data + ", size " + data.length + ", offset " + offset + ", length " + length + '\n' +" stream location " + Stream_Location () + " (" + streamPos + ')'); int amount = -1; if (Stream_Location () < Source_Size) { if (data == null) throw new NullPointerException (ID + '\n' + "Attempt to read into a null data array."); if (length < 0 || offset < 0 || offset >= data.length) throw new IndexOutOfBoundsException (ID + '\n' + "Attempt to read into array of length " + data.length + " at offset " + offset + " for amount " + length + '.'); if ((offset + length) > data.length) { length = data.length - offset; if ((DEBUG & DEBUG_IIS) != 0) System.out.println (" length clipped to array amount after offset: " + length); } int additional = length - Image_Buffer.remaining (); if (additional >= 0) { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (" additional buffer amount wanted: " + additional); if (Data_End_Location () < Source_Size) { // Round up to the nearest INPUT_INCREMENT. additional += INPUT_INCREMENT - (additional % INPUT_INCREMENT); if (Fetch (additional) <= 0) { if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.read: " + amount); return amount; } } } if (length > Image_Buffer.remaining ()) { length = Image_Buffer.remaining (); if ((DEBUG & DEBUG_IIS) != 0) System.out.println (" length clipped to buffer amount remaining: " + length); } // Required ImageInputStream functionality. bitOffset = 0; streamPos += length; Image_Buffer.get (data, offset, length); amount = length; } if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.read: " + amount); return amount; } /** Determine if the source data is cached.

@return true */ public boolean isCached () {return true;} /** Determine if the source data is cached in memory.

@return true */ public boolean isCachedMemory () {return true;} /** Get the size of the source file.

@return The size of the source file. This will be -1 if the size is not yet known. This information may become available after further interaction with the server providing access to the source file. */ public long length () { if (Source_Size == NO_SOURCE_SIZE) return -1; return Source_Size; } /** Discard buffered data before a source location.

@param location The source file offset before which buffered data is to be discarded. If the location is before the beginning of buffered data nothing is done. @throws IOException If the source file stream is closed. @throws IndexOutOfBoundsException If the location is greater than the current {@link #getStreamPosition() source input location}. @see #Drain(int) */ public void flushBefore ( long location ) throws IOException { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (">>> HTTP_ImageInputStream.flushBefore: " + location); if (location > Stream_Location ()) throw new IndexOutOfBoundsException (ID + '\n' + "Flush location " + location + " is beyond the current stream location of " + Stream_Location () + '.'); if (location > Buffer_Location ()) Drain (Position_for_Location (location)); if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.flushBefore"); } /** Set the current source file location where the next byte will be read.

The {@link #getBitOffset() bit offset} is set to zero.

@param location The source file offset for the image buffer's next read position. A location less than zero will be set to zero; a location greater than the source file size will be set to the end of the source file. @throws IOException If the source file stream is closed or there was a problem reading data from from the source. @see #Stream_Location(long) */ public void seek ( long location ) throws IOException { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (">>> HTTP_ImageInputStream.seek: " + location); Stream_Location (location); bitOffset = 0; if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.seek"); } /** All access to the source is closed.

@throws IOException If there was a problem closing the server input or output streams or the socket connection. */ public void close () throws IOException { if ((DEBUG & DEBUG_IIS) != 0) System.out.println (">>> HTTP_ImageInputStream.close"); String message = null; if (Server_Input != null) { try {Server_Input.close ();} catch (IOException exception) { message = ID + '\n' + "Unable to close the server input stream.\n" + exception.getMessage (); } } if (Server_Writer != null) { try {Server_Writer.close ();} catch (IOException exception) { if (message == null) message = ID; message += "\n" + "Unable to close the server output stream.\n" + exception.getMessage (); } } if (Server_Socket != null) { try {Server_Socket.setSoLinger (false, 0);} catch (SocketException exception) {} try {Server_Socket.close ();} catch (IOException exception) { if (message == null) message = ID; message += "\n" + "Unable to close the server socket connection.\n" + exception.getMessage (); } } Server_Socket = null; Server_Writer = null; Server_Input = null; Source_Size = NO_SOURCE_SIZE; System.gc (); if (message != null) throw new IOException (message); if ((DEBUG & DEBUG_IIS) != 0) System.out.println ("<<< HTTP_ImageInputStream.close"); } /** Checks if the server connection is closed.

@return true if the server connection is closed; false otherwise. */ public boolean is_Closed () {return Server_Socket == null;} /*============================================================================== HTTP Client */ /** Connect to the server.

The specified URL checked for validity. If the URL does not specify a port number, the {@link #DEFAULT_PORT} will be used.

N.B.: Establishing a new server connection causes any existing connection to be closed and the source input location to be {@link #Reset(long) Reset} to the beginning of the file.

@param url The URL to use in connection to the server. @throws IllegalArgumentException If the URL does not specify the http protocol nor provide a source file pathname. @throws UnknownHostException If the hostname of the URL can not be resolved to an internet address. @throws IOException If the socket connection could not be established or the input or output streams could not be opened. @see #Reconnect() */ public void Connect ( URL url ) throws IOException { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> HTTP_ImageInputStream.Connect: " + url); // Protocol. if (! "http".equals (url.getProtocol ())) { try {close ();} catch (IOException e) {} throw new IllegalArgumentException (ID + '\n' + "Invalid URL: " + url + '\n' + "Only the http protocol can be used."); } // Server hostname. String server_hostname = null; InetAddress address = null; try { server_hostname = url.getHost (); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Host name: " + server_hostname); address = InetAddress.getByName (server_hostname); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Host address: " + address); } catch (Exception exception) { try {close ();} catch (IOException e) {} throw new UnknownHostException (ID + '\n' + "Invalid URL: " + url + '\n' + "Unknown hostname."); } // Port number. int port = url.getPort (); if (port < 0) port = DEFAULT_PORT; if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Port number: " + port); // Image pathname. String source_pathname = url.getPath (); if (source_pathname.length () == 0 || source_pathname.equals ("/")) { try {close ();} catch (IOException e) {} throw new IllegalArgumentException (ID + '\n' + "Invalid URL: " + url + '\n' + "A source image pathname must be specified."); } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Image pathname: " + source_pathname); // Server internet socket address. InetSocketAddress server_address = Server_Address; Server_Address = new InetSocketAddress (address, port); // Connect (or reconnect) to the server. try {Reconnect ();} catch (IOException exception) { // Try to restore the previous server connection. Server_Address = server_address; try {Reconnect ();} catch (IOException except) {} throw exception; } // Update the server URL connection information. Source_URL = new URL ( url.getProtocol (), Server_Hostname = server_hostname, port, Source_Pathname = source_pathname ); if (Source_Buffer != null) Reset (0); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< HTTP_ImageInputStream.Connect"); } /** Reconnect to the server.

If a Socket for communication with the server exists it is closed.

A new Socket connection to the server is constructed. A maximum of {@link #DEFAULT_CONNECT_TIMEOUT} milliseconds is allowed for the connection to be established. Then input and output communication streams with the server are opened over the Socket. Up to {@link #MAX_RECONNECT_ATTEMPTS} connection attempts will be made. On the last attempt, the first connection operation failure that occurs will throw an IOException.

@throws IOException If a Socket could not be constructed on the server address within the {@link #DEFAULT_CONNECT_TIMEOUT} time, a Writer could not be constructed on the Socket or an InputStream could not be constructed on the Socket. */ protected void Reconnect () throws IOException { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> HTTP_ImageInputStream.Reconnect"); int attempt = MAX_RECONNECT_ATTEMPTS; while (attempt-- != 0) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Attempt " + (MAX_RECONNECT_ATTEMPTS - attempt)); // Close the current socket. if (Server_Socket != null) { if (Logging) System.out.println (LOG_OUTPUT_PREFIX + "[Server reconnect]"); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Closing the current connection"); try {close ();} catch (IOException exception) {} /* // Let the dust settle. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Pausing "+ RECONNECT_WAIT + " ms"); try {this.wait (RECONNECT_WAIT);} catch (InterruptedException exception) {} */ } if (Server_Address == null) // No addres for reconnection. break; if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Server Socket."); Server_Socket = new Socket (); try {Server_Socket.connect (Server_Address, DEFAULT_CONNECT_TIMEOUT);} catch (SocketTimeoutException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Socket connect timeout."); try {close ();} catch (IOException except) {} if (attempt == 0) throw new IOException (ID + '\n' + "Unable to connect to server at " + Server_Address + '\n' + "after waiting " + (DEFAULT_CONNECT_TIMEOUT / 1000) + " seconds.\n" + exception.getMessage ()); continue; } catch (IOException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" IOException: "+ exception.getMessage ()); try {close ();} catch (IOException except) {} if (attempt == 0) throw new IOException (ID + '\n' + "Unable to make a socket connection to the server at " + Server_Address + '\n' + exception.getMessage ()); continue; } // Output stream. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Server Writer."); try { Server_Writer = new BufferedWriter (new OutputStreamWriter (Server_Socket.getOutputStream (), ENCODING_SCHEME), HEADERS_BUFFER_SIZE); } catch (IOException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" IOException: "+ exception.getMessage ()); try {close ();} catch (IOException e) {} if (attempt == 0) throw new IOException (ID + '\n' + "Unable to open an output stream to the server at " + Server_Address + '\n' + exception.getMessage ()); continue; } // Input stream. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Server Input."); try {Server_Input = Server_Socket.getInputStream ();} catch (IOException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" IOException: "+ exception.getMessage ()); try {close ();} catch (IOException e) {} if (attempt == 0) throw new IOException (ID + '\n' + "Unable to open an input stream from the server at " + Server_Address + '\n' + exception.getMessage ()); continue; } break; } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< HTTP_ImageInputStream.Reconnect"); } /** Redirect the source URL.

@return true if the HTTP Status indicated a redirection (300-399) and the redirection succeeded; false otherwise. */ private boolean Redirect () throws IOException { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> HTTP_ImageInputStream.Redirect"); boolean redirected = false; if (HTTP_Status >= 300 && HTTP_Status < 400) { String source_location = Header_Content ("Location"); if (source_location != null) { Connect (new URL (source_location)); redirected = true; } } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< HTTP_ImageInputStream.Redirect: " + redirected); return redirected; } /*------------------------------------------------------------------------------ Buffer Management */ /** Source data buffer.

Two views of the same data are provided: A Source_Buffer and an Image_Buffer.

The Source_Buffer is filled (put) by the Server_Input socket stream. It's position is where the next data content - not header lines - input byte will be placed. It's limit is the buffer capacity.

The Image_Buffer is used (get) to implement the ImageInputStream interface. It's position is where the next ImageInputStream read byte will be obtained; this is equivalent to the virtual stream location in the {@link #Source_Pathname()} on the {@link #Server_Hostname()}. It's limit is the end of valid source data in the buffer (i.e. the buffer may not be full). This should be the same as the Source_Buffer position.

The first byte of the buffer is located at Buffer_Location offset in the source file. The streamPos variable in the ImageInputStreamImpl base class must also be maintained as the offset in the source file corresponding to the Image_Buffer position. */ private ByteBuffer Source_Buffer = null, Image_Buffer; // Requirement: INPUT_INCREMENT < Source_Buffer.capacity /** The amount of source data to {@link #Fetch(int) fetch} when performing an automatic refresh of buffer contents. */ public static final int INPUT_INCREMENT = 0x2000; // 8 KB. /** The default buffer capacity to {@link #Allocate_Buffer(int) allocate}. */ public static final int DEFAULT_BUFFER_SIZE = INPUT_INCREMENT << 4; // 128 KB. /** Allocates a data storage buffer.

A ByteBuffer of the specified size (bytes) is allocated and cleared (position set to zero and limit set to the capacity).

If there is no previously existing source buffer, the newly allocated buffer is set as the source buffer. It is duplicated to provide the image buffer view and the image buffer's limit is set to zero.

If there is a previously existing source buffer, all valid source data is copied from it into the new buffer, up to the capacity of the new buffer, and then the new buffer is set as the source buffer. N.B.: If the new buffer capacity is less than the previous buffer's valid data content, the excess data will be dropped. The source buffer is duplicated onto the temporary buffer, the image buffer limit and position copied over, and then the temporary buffer is set as the image buffer. N.B.: If the previous image buffer limit is greater than the new buffer capacity the new limit will be the new capacity; the same applies to the image buffer position, which would result in a corresponding move back of the logical stream location. Garbage collection is initiated to recover the previous buffer resources.

@param capacity The capacity of the new buffer. If less than twice the {@link #INPUT_INCREMENT} it will be increased to this value. */ public void Allocate_Buffer ( int capacity ) { if (capacity < (2 * INPUT_INCREMENT)) capacity = 2 * INPUT_INCREMENT; if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (">>> HTTP_ImageInput_Stream.Allocate_Buffer: " + capacity); ByteBuffer buffer = ByteBuffer.allocate (capacity); buffer.clear (); // position = 0; limit = capacity. if (Source_Buffer == null) { Source_Buffer = buffer; Image_Buffer = Source_Buffer.duplicate (); Image_Buffer.limit (0); } else { // Copy over the existing data. if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" Copying " + Source_Buffer.position () + " data buffer bytes"); Source_Buffer.flip (); if (Source_Buffer.limit () > buffer.capacity ()) Source_Buffer.limit (buffer.capacity ()); buffer.put (Source_Buffer); Source_Buffer = buffer; // Replicate the image buffer view. buffer = Source_Buffer.duplicate (); if (Image_Buffer.position () > buffer.capacity ()) { buffer.position (buffer.capacity ()); streamPos = Location_for_Position (buffer.capacity ()); } else buffer.position (Image_Buffer.position ()); if (Image_Buffer.limit () > buffer.capacity ()) buffer.limit (buffer.capacity ()); else buffer.limit (Image_Buffer.limit ()); Image_Buffer = buffer; // Free unused memory. System.gc (); } if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println ("<<< HTTP_ImageInput_Stream.Allocate_Buffer"); } /** Drain data from the buffer.

The logical location of the buffer in the source data stream is moved forward by some amount. This has the effect of draining valid source data from the front of the buffer and sliding the buffer forward to a new location in the source file.

If the amount is less than or equal to zero, nothing is done.

If the amount is greater than or equal to the end of valid source data in the buffer (image buffer limit), the buffer is simply (@link #Empty() emptied}.

The remaining source data beyond the amount buffer index is moved to the beginning of the buffer. The buffer's logical position is the source data stream is moved forward by the amount of data removed (the slide distance). The source buffer input position and image buffer limit are reset to the new end of valid source data. The image buffer position is moved back by the amount of of data removed. However, if this new position would be less than zero it is set to zero, and the current logical stream location is moved up by the difference (i.e. to the buffer's logical position in the stream).

@param amount The amount of data to drain. @return The amount of source data effectively removed from the buffer (i.e. the slide distance). */ public int Drain ( int amount ) { if (amount <= 0) return 0; if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Drain: " + amount); /* N.B.: Image_Buffer limit == Source_Buffer position And they must remain the same. */ if (amount >= Image_Buffer.limit ()) { // Empty the buffer. if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Drain: emptied"); return Empty (); } // Post-move location in the source data stream of the buffer. Buffer_Location (Buffer_Location () + amount); // Post-move current stream location buffer index. int position = Image_Buffer.position () - amount; if (position < 0) { position = 0; /* Data is being drained beyond the current stream location. The stream location must be shifted up accordingly. */ streamPos = Buffer_Location (); } // Post-move end of valid source data buffer index. int limit = Image_Buffer.limit () - amount; // Move the data. if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" Moving data buffer bytes [" + amount + '-' + Source_Buffer.position () + ") to [0-" + Source_Buffer.position () + ')'); Source_Buffer.limit (Source_Buffer.position ()); // End of data. Source_Buffer.position (amount); // Start of data. Image_Buffer.position (0); // Destination. Image_Buffer.put (Source_Buffer); // Move the data. // Reset the pointers. Image_Buffer.position (position); Image_Buffer.limit (limit); Source_Buffer.position (limit); Source_Buffer.limit (Source_Buffer.capacity ()); if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" Source_Buffer.position = " + Source_Buffer.position () + '\n' +" Source_Buffer.limit = " + Source_Buffer.limit () + '\n' +" Image_Buffer.position = " + Image_Buffer.position () + '\n' +" Image_Buffer.limit = " + Image_Buffer.limit () + '\n' +" Buffer_Location = " + Buffer_Location () + '\n' +" streamPos = " + streamPos + '\n' +"<<< HTTP_ImageInputStream.Drain: " + amount); return amount; } /** Drain source data before the current stream position.

This has the same effect as {@link #Drain(int) drain}ing all data before the current stream (image buffer) position.

@return The amount of source data effectively removed from the buffer (i.e. the slide distance). @see #Drain(int) */ public int Drain () {return Drain (Image_Buffer.position ());} /** Empty the buffer of all source data.

The buffer's logical location in the source data stream is moved up to the end of source data content in the buffer, and the current source data stream position is set to this location. The source buffer position is set to the beginning of the buffer as are the image buffer position and data limit. This has the same effect as {@link #Drain(int) drain}ing all the data content from the buffer.

@return The amount of source data effectively removed from the buffer (i.e. the slide distance). */ public int Empty () { int amount = Image_Buffer.limit (); if (amount > 0) Reset (Data_End_Location ()); return amount; } /** Reset the buffer to a new source location and empty it.

: All buffered source data is dropped regardless of the location relative to the current buffered source data range.

@param location The new buffer and stream position location. */ public void Reset ( long location ) { Buffer_Location = streamPos = location; Source_Buffer.position (0); Image_Buffer.limit (0); } /** Get the virtual source location of the beginning of the buffer.

@return The source file offset where the buffer begins. */ public long Buffer_Location () {return Buffer_Location;} /** Set the virtual source location of the beginning of the buffer.

N.B.: Changing the virtual source location of the buffer will affect all other buffer virtual locations because they are relative to the buffer location.

@param location The source file offset where the buffer begins. A value less than zero is set to zero; a value greater than the size of the source file is set to the size of the source file. */ protected void Buffer_Location ( long location ) { if (location < 0) location = 0; else if (location > Source_Size) location = Source_Size; Buffer_Location = location; } /** Get the virtual source location where the next image buffer will be read.

@return The current image buffer virtual source file offset location. */ protected long Stream_Location () {return Location_for_Position (Image_Buffer.position ());} /** Set the virtual source location where the next image buffer will be read.

If the location is within the range of buffered source data the image buffer position is simply moved to the corresponding position.

The location is less than zero, zero will be used; if it is greater than the size of the source file, the location will be set to the end of the source data.

If the location is the same as the current virtual source stream location nothing is done.

If the location is before the stream location of the first byte in the image buffer and within {@link #INPUT_INCREMENT} bytes of the beginning of the buffer, the current buffered data will be shifted up by INPUT_INCREMENT bytes and the resulting gap at the beginning of the buffer filled with data {@link #Fetch(int) Fetch}ed from the corresponding offset range of the source file. The image buffer position is then set to correspond to the new stream location.

If the location is beyond the end of buffered data, but not beyond the end of the maximum capacity of the buffer, and within INPUT_INCREMENT bytes of the end of data, up to INPUT_INCREMENT additional bytes (not more than would fill the buffer to capacity) are {@link #Fetch(int) Fetch}ed from the source file to extend the buffered data. The image buffer position is then set to correspond to the new stream location.

For any other case the buffer is simply {@link #Reset(long) Reset} to the new location.

The effect is a random access seek to any location within the source file, with data pre-fetch optimization when the new location is near the currently buffered data.

@param location The source file offset for the image buffer's next read position. */ public void Stream_Location ( long location ) throws IOException { if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Stream_Location: " + location); if (location >= Buffer_Location () && location <= Data_End_Location ()) { // The new location is within the available buffered data. Image_Buffer.position ((int)(location - Buffer_Location ())); streamPos = location; if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" The location is within the buffer.\n" +"<<< HTTP_ImageInputStream.Stream_Location: " + streamPos); return; } if (location < 0) location = 0; else if (location > Source_Size) location = Source_Size; if (location == streamPos) { if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" The location is unmoved.\n" +"<<< HTTP_ImageInputStream.Stream_Location: " + streamPos); return; } if (location < Buffer_Location ()) { // Move the logical stream location back before the buffer location. if ((Buffer_Location () - location) <= INPUT_INCREMENT) { /* The seek distance is no greater than the input increment. Move data an input increment to prevent inefficient "creep". Make a hole for the input data by shifting the existing data up so that it will be contiguous with the new data that is read into the hole. Data is moved from the upper end to prevent data overlap. */ int source = Image_Buffer.limit () - 1, // Source byte index. target = source + INPUT_INCREMENT; // Destination index. if (target >= Image_Buffer.capacity ()) { // Clip off the excess data. source -= target - Image_Buffer.capacity () + 1; target = Image_Buffer.capacity () - 1; } Image_Buffer.limit (target + 1); // New end of data index. // Move the data. if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" Moving data buffer bytes [0-" + (source + 1) + ") to [" + (Image_Buffer.limit () - source) + '-' + (target + 1) + ')'); while (source >= 0) Image_Buffer.put (target--, Image_Buffer.get (source--)); // Fill the hole with an input increment of source data. target = Image_Buffer.limit (); // Save end of data position. Reset (Buffer_Location () - INPUT_INCREMENT); Fetch (INPUT_INCREMENT); // Reset the buffer pointers. Source_Buffer.position (target); // Restore end of data position. Image_Buffer.limit (target); Image_Buffer.position (Position_for_Location (location)); streamPos = location; } else // The seek distance is larger than the input increment. Reset (location); } else { // Move the logical location forward beyond the end of data. if (location < Location_for_Position (Image_Buffer.capacity ()) && (location - INPUT_INCREMENT) < Data_End_Location ()) { // The new location is within an INPUT_INCREMENT of end of data. int amount = INPUT_INCREMENT; if ((Image_Buffer.limit () + amount) > Image_Buffer.capacity ()) amount = Image_Buffer.capacity () - Image_Buffer.limit (); Fetch (amount); Image_Buffer.position (Position_for_Location (location)); streamPos = location; } else // The seek distance is larger than the input increment. Reset (location); } if ((DEBUG & DEBUG_BUFFERS) != 0) System.out.println (" Source_Buffer.position = " + Source_Buffer.position () + '\n' +" Source_Buffer.limit = " + Source_Buffer.limit () + '\n' +" Image_Buffer.position = " + Image_Buffer.position () + '\n' +" Image_Buffer.limit = " + Image_Buffer.limit () + '\n' +" Buffer_Location = " + Buffer_Location () + '\n' +" streamPos = " + streamPos + '\n' +"<<< HTTP_ImageInputStream.Stream_Location: " + streamPos); } /** Get the virtual source location of the end (exclusive) of buffered data.

@return The source file offset where buffered data ends. */ public long Data_End_Location () {return Location_for_Position (Image_Buffer.limit ());} /** Set the virtual source location of the end (exclusive) of buffered data.

N.B.: If the new location is less than the buffer's current virtual source location, the virtual source location will also be moved to the new location. This enforces the constraint that the virtual source location is always less than or equal to the end of data location.

@param location The source file offset where the end of buffered data is located. The location is clipped to produce a buffer position with the range of the buffer capacity. */ protected void Data_End_Location ( long location ) { int index = Position_for_Location (location); if (index < 0) index = 0; else if (index > Image_Buffer.capacity ()) index = Image_Buffer.capacity (); Image_Buffer.limit (index); Source_Buffer.position (index); } /** Convert a buffer position to a virtual source location.

@param position A buffer position int. @return A source location long. */ protected long Location_for_Position ( int position ) {return Buffer_Location () + position;} /** Convert a virtual source location to a buffer position.

@param location A source location long. @return A buffer position int. */ protected int Position_for_Location ( long location ) {return (int)(location - Buffer_Location ());} /** Clip a source location to the currently buffered data range.

@param location A source location long. @return The source location clipped to the range of buffered data. */ public long Image_Buffer_Range ( long location ) { if (location < Buffer_Location ()) return Buffer_Location (); if (location < Data_End_Location ()) return location; return Data_End_Location (); } /** Test if a source location is in the range of buffered data.

The buffered data range begins at the {@link #Buffer_Location()} (inclusive) and extends to the {@link #Data_End_Location()} (exclusive).

@param location A source location long. @return true if the location is the range of buffered data; false otherwise. */ public boolean in_Image_Buffer_Range ( long location ) { return location >= Buffer_Location () && location < Data_End_Location (); } /*------------------------------------------------------------------------------ Request */ /** Check the server status of the source.

A "HEAD" request for the source file is made to the server. The reponse to a "HEAD" request will contain all the HTTP headers of a normal request, but no data content will be included.

If access to the server is closed, then the last HTTP status value will be returned. If no HTTP status value was ever obtained zero will be returned. If an end of file condition is encountered will communicating with the server, -1 will be returned. If some other IOException is thrown, -2 will be returned. If any other Exception is thrown, -3 will be returned.

The HTTP status when the source file is correctly found is less than {@link #SUCCESS_STATUS_LIMIT}. If the source file is not found the status will be {@link #SOURCE_NOT_FOUND_STATUS}.

@return The HTTP response status. */ public int Check_Source () { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> HTTP_ImageInputStream.Check_Source"); if (! is_Closed ()) { try { Request ("HEAD", Source_Pathname); End_Request (); Headers (); } catch (Exception exception) { HTTP_Status = ((exception instanceof EOFException) ? -1 : ((exception instanceof IOException) ? -2 : -3 )); } } if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< HTTP_ImageInputStream.Check_Source: " + HTTP_Status); return HTTP_Status; } /** Fetch source data from the server (absolute fetch).

The data will be fetched from the source file beginning at the start location up to, but not including, the end location.

If the start location is within the buffered source data range it is moved up to the end of data location. If the start location is at or beyond the end of the source file, nothing is done and zero is returned. If, after range clipping, the start location is not the same as the end of source data location in the buffer, the buffer is is {@link #Reset(long) reset} to the start location. The end location will be clipped to the size of the source file.

If communication with the server is lost during the data fetch, a {@link #Reconnect() reconnect}ion will be tried. If this succeeds the data fetch will be repeated.

@param start The source file offset (inclusive) where the data fetch is to begin. @param end The source file offset (exclusive) where the data fetch is to end. @return The amount of data received from the server. @throws IllegalArgumentException If start is less than zero or greater than end. @throws IOException If there was a problem communicating with the server. @see #Request_Source_Data(long, long) @see #Receive() */ public int Fetch ( long start, long end ) throws IllegalArgumentException, IOException { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> HTTP_ImageInputStream.Fetch: " + start + '-' + end); if (Server_Writer == null) throw new IOException (ID + '\n' + "The server connection is closed."); int received = 0; if (start < 0 || start > end) throw new IllegalArgumentException (ID + '\n' + "Invalid source data range to fetch: " + start + '-' + end); if (in_Image_Buffer_Range (start)) start = Data_End_Location (); if (start < Source_Size) { if (end > Source_Size) end = Source_Size; if (start != Data_End_Location ()) // Reset the buffer to the new start location. Reset (start); Request_Source_Data (start, end); received = Receive (); if (received < 0) { try { Reconnect (); Request_Source_Data (start, end); received = Receive (); if (received < 0) { throw new IOException (ID + '\n' + "Sever connection lost while fetching from " + start + " to " + end); } } catch (IOException exception) { throw new IOException (exception.getMessage () + '\n' + "Sever connection lost while fetching from " + start + " to " + end); } } } if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< HTTP_ImageInputStream.Fetch: " + received); return received; } /** Fetch source data from the server (relative fetch).

The data will be fetched from the source file starting at the current end of source data location in the buffer.

@param amount The amount of data to fetch. @return The amount of data fetched. @throws IOException If there was a problem send or receiving data from the server. */ public int Fetch ( int amount ) throws IOException {return Fetch (Data_End_Location (), Data_End_Location () + amount);} /** Get a range of source data.

N.B.: No check is made of the validity of the source range.

@param start The file offset of the start of the data range (inclusive). @param end The file offset of the end of the data range (exclusive). @return This HTTP_ImageInputStream. @throws IllegalArgumentException If start is greater than end. @throws BufferOverflowException If the (end - start) amount is greater than Integer.MAX_VALUE. @throws IOException If there was a problem sending the request to the server. */ protected HTTP_ImageInputStream Request_Source_Data ( long start, long end ) throws IllegalArgumentException, BufferOverflowException, IOException { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> HTTP_ImageInputStream.Request_Source_Data: " + start + "-" + end); if (start > end) throw new IllegalArgumentException (ID + '\n' + "Invalid source data range request: " + start + "-" + end); if ((end - start) > (long)Integer.MAX_VALUE) throw new BufferOverflowException (); if (start < end) { Request ("GET", Source_Pathname); // N.B.: Request range limits are inclusive. Send ("Range: bytes=" + start + "-" + --end); Send ("Accept-Encoding: identity"); End_Request (); } if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< HTTP_ImageInputStream.Request_Source_Data"); return this; } /** Issue a Request preamble to the server.

After the HTTP formatted request is {@link #Send(String) sent} to the server, the Host, User-Agent, and Cache-Control (no-cache) requests are also sent. N.B.: The request sequence has not been {@link #End_Request() ended}.

@param method The request method String. This is typically "GET". @param request The request operation. @see #Send(String) */ protected void Request ( String method, String request ) throws IOException { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> HTTP_ImageInputStream.Request: " + method + ' ' + request); Send (method + ' ' + request + ' ' + HTTP_VERSION); Send ("Host: " + Server_Hostname + ':' + Server_Address.getPort ()); Send ("User-Agent: " + User_Agent); Send ("Cache-Control: no-cache"); if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< HTTP_ImageInputStream.Request"); } /** Send the end-of-request sequence to the server.

An single empty line is sent. */ protected void End_Request () throws IOException {Send ("");} /** Send a message line to the server.

The message is expected to be a single line to be sent to the server. An end of line sequence will be appended to the message that is sent if it is not already present.

If {@link #Logging(boolean) Logging} is enabled the message, preceded by the {@link #LOG_OUTPUT_PREFIX}, will be printed to stdout before it is sent to the server.

@param message The message String to be sent. @throws IOException If the connection to the server is closed or there was a problem sending the message. */ protected void Send ( String message ) throws IOException { if (is_Closed ()) throw new IOException (ID + '\n' + "The server connection is closed."); if (message.length () == 0 || message.charAt (message.length () - 1) != '\n') message += EOL; Server_Writer.write (message); Server_Writer.flush (); Total_Sent += message.length (); if (Logging) System.out.print (LOG_OUTPUT_PREFIX + message); } /*------------------------------------------------------------------------------ Response */ /** Receive a response to a server request.

Server interaction always occurs in {@link #Request(String,String) Request}-Receive pairs. After a Request has been sent it is necessary to Receive the response from the server. The first part of the response is sequence of one or more HTTP header lines. If a Content-Length line is present the header lines are followed by data content of Content-Length bytes.

If the HTTP response headers contain a {@link #Status() Status} value that is less than 300, the data content is read into the Source_Buffer starting at its current position. However, if the amount of free space remaining in the buffer (buffer storage not occupied by valid data content; i.e. locations [position, limit), where limit == capacity) is less than the Content-Length, more buffer space is first provided.

Space to store data content in the buffer is provided by {@link #Drain() drain}ing away data from the beginning of the buffer before the current source stream position.

@return The amount of source data received, or -1 if EOF was encountered before any data was received. */ protected int Receive () throws IOException { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (">>> HTTP_ImageInputStream.Receive"); // Get the HTTP headers. try {Headers ();} catch (EOFException exception) { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println ("<<< HTTP_ImageInputStream.Receive: -1"); return -1; } long content_length = Content_Length (); if (content_length > Integer.MAX_VALUE) { /* Too much data content! This should never happen. But if it does something is wrong and the Server_Input is now jammed with a huge queue of data that must be dumped. Instead of locking up the user application needlessly reading it all in, the connection to the server will be closed and a BufferOverflowException thrown. */ close (); throw new BufferOverflowException (); } Range range = Content_Range (); int amount, received = 0, length = (int)content_length; if (length > 0) { if (Status () < 300) { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Response status " + Status () + '\n' +" Content range: " + range + '\n' +" Content length: " + length); if (range.Start != Data_End_Location ()) { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Content is not contiguous with buffer content.\n" +" Buffer reset to location " + range.Start); Reset (range.Start); } if (length > Source_Buffer.remaining ()) { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Only " + Source_Buffer.remaining () + " bytes of free buffer space remaining."); // More buffer space needed. Drain the buffer. Drain (); if (length > Source_Buffer.remaining ()) { /* Even more buffer space needed. Allocate a larger buffer that is a multiple of the INPUT_INCREMENT. */ amount = length - Source_Buffer.remaining (); amount += Source_Buffer.capacity () + INPUT_INCREMENT - (amount % INPUT_INCREMENT); Allocate_Buffer (amount); } } while (received < length) { amount = length - received; if ((amount = Server_Input.read (Source_Buffer.array (), Source_Buffer.position (), amount)) < 0) break; if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Got " + amount + " -\n" +"-->|" + new String_Buffer (new String (Source_Buffer.array (), Source_Buffer.position (), amount)) .special_to_escape () + "|<--"); Data_End_Location (Data_End_Location () + amount); received += amount; Total_Received += amount; } if (Logging) System.out.println (LOG_INPUT_PREFIX + "Received " + received + " of " + length + " bytes."); } else { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Response status " + Status () + '\n' +" Skipping " + length + " bytes."); Server_Input.skip (length); } } if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println ("<<< HTTP_ImageInputStream.Receive: " + received); return received; } /*------------------------------------------------------------------------------ HTTP Response Headers */ /** Get the HTTP response headers from the server.

After a {@link #Request(String, String) Request} has been sent to the server - probably followed by {@link #Send(String) Send}ing additional request messages - and {@link #End_Request() End}ed, the server response will begin with a sequence of HTTP headers. Each response line up to, but not including, an empty line will be added to a Vector of HTTP Headers in which each element is itself a Vector containing the header name and content. Note: A header line can be wrapped across multiple response lines; the wrapped line(s) begin with whitespace. In addition, a header with the same name as a previous header will have its content appended to previous header. This guarantees that each header in the Vector of HTTP Headers will have a unique name.

Before the header lines are accumulated the HTTP Headers Vector is cleared and the HTTP response {@link #Status() Status} is reset to zero. As the header lines are being accumulated the HTTP response status will be updated if it is found. In addition, the {@link #Source_Size() source file size} will also be set if it is found the response.

@return The HTTP response status. @throws IOException If an problem was encountered while reading response lines. This will be an EOFException if an end of input from the server was encountered. */ protected int Headers () throws IOException { if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Headers"); // Reset for new headers from the server. HTTP_Headers.clear (); HTTP_Status = 0; String line, header = ""; while (true) { // Get a line from the server response. line = Response_Line (); if (line.length () == 0 || WHITESPACE.indexOf (line.charAt (0)) < 0) { // Start of a new header. Add the previous header. Header_Add (header); if (line.length () == 0) // Last line. break; // Reset the header to the current line. header = line; } else { // Continuation of wrapped line. if (header.length () == 0) /* The first line can't be wrapped. Set the header to the current line trimmed of whitespace. */ header = line.trim (); else // Append this line to the header. header += line; } } // Attempt to update the HTTP response status. Status (); if (Source_Size == NO_SOURCE_SIZE) // Attempt to set the source file size. Source_Size (); if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Headers: " + HTTP_Status); return HTTP_Status; } /** A server response header line is read from the server.

One byte at a time is read from the Server_Input until an end-of-line or end-of-file condition is encountered. All bytes up to, but not including, the end-of-line delimiter(s) are accumulated as characters in a StringBuffer.

An end-of-line condition is identified by a '\n' or '\r' character. A '\r' may optionally be followed by a '\n' character which is treated as an additional end-of_line delimiter. The end-of-line characters are not included in the returned String. The Server_Input is left positioned at the next byte after the end-of-line delimiter(s).

If an end-of-file condition is encountered after one or more line characters have been accumulated, a String is returned with those characters. However, if no characters have been accumulated an EOFException is thrown.

@return A String containing the line that was read. @throws IOException if there was a problem reading from the Server_Input. This will ba an EOFException if an end-of-file condition was encountered before any line characters were read. */ private String Response_Line () throws IOException { StringBuffer line = new StringBuffer (); int datum; Line: while ((datum = Server_Input.read ()) >= 0) { ++Total_Received; switch (datum) { case '\r': // Probable NL after CR. Server_Input.mark (1); if ((datum = Server_Input.read ()) >= 0 && datum != '\n') { // Push the byte back onto the stream. Server_Input.reset (); --Total_Received; } case '\n': break Line; default: line.append ((char)datum); } } if (datum < 0) { // EOF if (line.length () == 0) throw new EOFException (ID + '\n' + "Unexpected server disconnect."); } if (Logging) System.out.println (LOG_INPUT_PREFIX + line); return line.toString (); } /** Add a field String to the HTTP Headers Vector.

The first word of the field String is the header name; the remainder is the header content (this may be empty). If a header by the same name already exists in the HTTP Headers Vector, its content is {@link #Header_Append(String, String) appended} with the new content.

@param field_string The HTTP header line String. */ private void Header_Add ( String field_string ) { if (field_string == null || field_string.length () == 0) return; // Break the field string into a field name-content vector. Words words = new Words (field_string) .Delimiters (field_string.startsWith ("HTTP") ? HTTP_VERSION_DELIMITERS : FIELD_CONTENT_DELIMITERS); Vector field = words.Split (2); if (field.size () != 0) { if (field.size () == 1) // Provide empty content. field.add (""); if (! Header_Append ((String)field.get (0), (String)field.get (1))) // New field. HTTP_Headers.add (field); } } /** Append header content to an existing header.

If a header with the name exists in the HTTP Headers Vector it's content is appended, after a space character, with the content String.

@param name The name of a header to have its content appended. @param content The content String to be appended. @return true if a header with the name was found; false otherwise. */ private boolean Header_Append ( String name, String content ) { if (name == null || content == null || content.length () == 0) return false; int index = Header_Index (name); if (index < 0) return false; Vector field = (Vector)HTTP_Headers.get (index); /* N.B.: It is assumed that the field will always have field name and field content elements, even if one or both are empty. */ field.set (1, (String)field.get (1) + ' ' + content); return true; } /** Get the HTTP Headers Vector index for a named header.

@param name The name of a header. @return The HTTP Headers Vector index for the named header, or -1 if the header name was not found. */ private int Header_Index ( String name ) { if (name != null) { int index = 0, total = HTTP_Headers.size (); while (index < total) { /* N.B.: It is assumed that the field will always have a field name element, even if it is empty. */ Vector field = (Vector)HTTP_Headers.get (index); if (name.equalsIgnoreCase ((String)((Vector)HTTP_Headers.get (index)).get (0))) return index; ++index; } } return -1; } /** Get the header content for a named header.

@param name The name of a header. @return The content String for the named header, or null if the header name was not found. */ public String Header_Content ( String name ) { String content = null; int index = Header_Index (name); if (index >= 0) /* N.B.: It is assumed that the field will always have field name and field content elements, even if one or both are empty. */ content = (String)((Vector)HTTP_Headers.get (index)).get (1); return content; } /** Get the complete header field for a named header.

The header field is provided in the same syntax as it was received from the server. However, any header wrapping has been removed. If multiple headers with the same name were received, a single header field with all content appended in the order it was received is provided.

@param name The name of a header. @return The complete header field String. */ public String Header_Field ( String name ) { String content = Header_Content (name); if (content != null); name += (name.equalsIgnoreCase ("HTTP") ? HTTP_VERSION_DELIMITERS.charAt (0) : FIELD_CONTENT_DELIMITERS.charAt (0)) + ' ' + content; return name; } /** Get a header field value by name.

A header field is divided into value fields by any one of the ':', ';', ',', '&' or whitespace characters. A value field occurs in in header content as a value name separated from its value by an '=' character.

@param name The name of a header. @param value_name The name of a content value. @return The value String. This will be null if the header or value name is not found. */ public String Header_Value ( String name, String value_name ) { if (value_name == null) return null; String value = null, content = Header_Content (name); if (content != null) { Vector values = new Words (content) .Delimiters (FIELD_CONTENT_DELIMITERS) .Split (); for (int index = 0, total = values.size (); index < total; ++index) { Vector field_value = new Words ((String)values.get (index)) .Delimiters (VALUE_DELIMITERS) .Split (2); if (field_value.size () == 0) // Empty value! continue; String key = (String)field_value.get (0); if (value_name.equalsIgnoreCase (key)) { if (field_value.size () == 1) // Only a value. value = key; else // key=value. value = ((String)field_value.get (1)).trim (); break; } } } return value; } /** Get a header field value by index within the field content.

@param name The name of a header. @param value_index An index int, starting with zero for the first value in the content. @return The value String. This will be null if the header or value is not found. @see #Header_Value(String, String) */ public String Header_Value ( String name, int value_index ) { if (value_index < 0) return null; if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Header_Value: " + name + ' ' + value_index); String value = null, content = Header_Content (name); if (content != null) { Vector values = new Words (content) .Delimiters (FIELD_CONTENT_DELIMITERS) .Split (); if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (" HTTP_ImageInputStream.Header_Value:\n" + " values: " + values); if (values.size () > value_index) { values = new Words ((String)values.get (value_index)) .Delimiters (VALUE_DELIMITERS) .Split (2); if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (" value: " + values); if (values.size () == 0) // Empty value! value = ""; else if (values.size () == 1) // Only a value. value = (String)values.get (0); else // key=value. value = ((String)values.get (1)).trim (); } } if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Header_Value: " + value); return value; } /** Get the HTTP status of the last response from the server.

The HTTP status is expected to be the second field value of the "HTTP" response header.

@return The HTTP status value. This will be zero if no HTTP status has yet been found. It will be negative if the expected HTTP status field does not contain an integer value. */ public int Status () { if (HTTP_Status > 0) return HTTP_Status; if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Status"); String status_string = Header_Value ("HTTP", 1); if (status_string != null) { try {HTTP_Status = Integer.parseInt (status_string);} catch (NumberFormatException exception) {HTTP_Status = -2;} } if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Status: " + HTTP_Status); return HTTP_Status; } /** Get the size of the source file.

If the source file size has already been determined the cached value is returned.

The size of the source file is obtained from the {@link #Content_Range() "Content-Range"} HTTP response header. If a valid size value is found the cached value is updated and that value returned.

@return The size of the source file. This will be {@link #NO_SOURCE_SIZE} if the size is unknown. */ public long Source_Size () { if (Source_Size != NO_SOURCE_SIZE) return Source_Size; if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Source_Size"); Range range = Content_Range (); if (range != null && range.Length >= 0) Source_Size = range.Length; if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Source_Size: " + Source_Size); return Source_Size; } /** Get the length of server response data content.

The length of the data content is expected to be the first field of the "Content-Length" HTTP response header.

@return The long value length of the response data content. This will be -1 if the content length could not be found. It will be -2 if the expected HTTP header field does not contain an valid value. */ public long Content_Length () { if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Content_Length"); long length = -1; String length_string = Header_Value ("Content-Length", 0); if (length_string != null) { try {length = Long.parseLong (length_string);} catch (NumberFormatException exception) {length = -2;} } if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Content_Length: " + length); return length; } /** Get the Content-Range header values.

The format of the Content-Range header is:

Content-Range: bytes start-end/length

Where and are byte offsets into the source file for the start and end (inclusive) of the bytes returned. The value is the total length of the source file.

@return A Range containing the Content-Range values, or null if no Content-Range header could be found. Missing values in the range are indicated by the value -1. */ public Range Content_Range () { if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println (">>> HTTP_ImageInputStream.Content_Range"); Range range = null; String range_string = Header_Value ("Content-Range", 1); Values: if (range_string != null) { range = new Range (-1, -1, -1); int first = 0, last; // Start if ((last = range_string.indexOf ('-')) <= 0) break Values; try {range.Start = Long.parseLong (range_string.substring (first, last));} catch (NumberFormatException exception) {break Values;} // End first = ++last; if ((last = range_string.indexOf ('/')) <= 0) { // Try using the remainder of the string. if ((last = range_string.length ()) == first) // No remainder. break Values; } try {range.End = Long.parseLong (range_string.substring (first, last));} catch (NumberFormatException exception) {break Values;} // Length first = ++last; if (first < range_string.length ()) { try {range.Length = Long.parseLong (range_string.substring (first));} catch (NumberFormatException exception) {} } } if ((DEBUG & DEBUG_HEADERS) != 0) System.out.println ("<<< HTTP_ImageInputStream.Content_Range: " + range); return range; } /** A Range contains Start, End and Length values.

The Length is not necessarily the length between Start and End. There are no constraints on any of the values. */ public class Range { public long Start = 0, End = 0, Length = 0; Range ( long start, long end, long length ) { Start = start; End = end; Length = length; } public String toString () { return Long.toString (Start) + '-' + End + '/' + Length; } } /*============================================================================== Application main */ public static void main ( String[] args ) { if (DEBUG != DEBUG_OFF) System.out.println ("*** " + ID + " ***"); String connection_URL = null; boolean logging = false; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].charAt (1)) { case 'L': case 'l': logging = true; break; case 'H': case 'h': default: Usage (); } } else { if (connection_URL != null) { System.out.println ("Multiple connection URLs specified -\n" +" " + connection_URL + " and\n" + args[count]); Usage (); } connection_URL = args[count]; } } if (connection_URL == null) Usage (); try { HTTP_ImageInputStream source = new HTTP_ImageInputStream (new URL (connection_URL)); source.Logging (logging); if (DEBUG != DEBUG_OFF) source.Logging (true); System.out.println ("Check source status: " + source.Check_Source ()); } catch (Exception exception) { System.out.println ("!!! " + exception); System.exit (1); } } public static void Usage () { System.out.println ( ID + '\n' + "Usage: HTTP_ImageInputStream \n" ); System.exit (1); } } pirl-2.3.8/PIRL/Image_Tools/JP2_Info0000755000175000017500000000107111600760723016611 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the JP2_Info Java application. # # CVS ID: JP2_Info,v 1.3 2011/06/24 01:01:07 castalia Exp $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); $CLASSPATH = $ENV{"CLASSPATH"}; if ($PIRL_JAVA_HOME) { if ($CLASSPATH) { $CLASSPATH = "$PIRL_JAVA_HOME:$CLASSPATH"; } else { $CLASSPATH = $PIRL_JAVA_HOME; } } @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Image_Tools.JP2_Info", @ARGV; exec @Arguments; pirl-2.3.8/PIRL/Image_Tools/JPEG2000_Codestream_Info0000755000175000017500000000115111743637607021426 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the JPEG2000_Codestream_Info Java application. # # CVS ID: JPEG2000_Codestream_Info,v 1.1 2012/04/18 22:26:15 castalia Exp $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); $CLASSPATH = $ENV{"CLASSPATH"}; if ($PIRL_JAVA_HOME) { if ($CLASSPATH) { $CLASSPATH = "$PIRL_JAVA_HOME:$CLASSPATH"; } else { $CLASSPATH = $PIRL_JAVA_HOME; } } @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Image_Tools.JPEG2000_Codestream_Info", @ARGV; exec @Arguments; pirl-2.3.8/PIRL/Image_Tools/Equirectangular_Projection.java0000644000175000017500000001471211742733714023524 0ustar mathieumathieu/* Equirectangular_Projection PIRL CVS ID: Equirectangular_Projection.java,v 1.4 2012/04/16 06:10:20 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import java.awt.Point; import java.awt.geom.Point2D; public class Equirectangular_Projection extends Projection { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Equirectangular_Projection (1.4 2012/04/16 06:10:20)"; private static final String PROJECTION_NAME = "Equirectangular"; // Pre-computed values. private double Center_Latitude_Radius, Coefficient_CLR; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_CONVERTERS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Equirectangular_Projection ( Parameter parameters ) throws PVL_Exception, UnsupportedOperationException { // Initialize the base projection parameters. super (parameters); if (Not_Identity) { Center_Latitude_Radius = Local_Radius (Center_Latitude); if (Center_Latitude == PI_OVER_2) throw new UnsupportedOperationException (ID + '\n' + PROJECTION_NAME + " projection can't be done\n" + "when the " + CENTER_LATITUDE_PARAMETER_NAME + " parameter value is " + Math.toDegrees (Center_Latitude) + " degrees."); Coefficient_CLR = Center_Latitude_Radius * StrictMath.cos (Center_Latitude); } } /*============================================================================== Accessors */ public String Name () {return PROJECTION_NAME;} /*============================================================================== Converters */ /** Get the world longitude,latitude coordinate for an image sample,line coordinate.

The conversion algorithm is:

longitude = Center_Longitude + x / (R * cos (Center_Latitude))
latitude = y / R
where R is the {@link Projection#Local_Radius(double) radius} of the body at the Center_Latitude.

@param image_coordinate The image sample,line coordinate. @return The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees within the range 0-360. */ public Point2D.Double to_World ( Point2D image_coordinate ) throws IllegalArgumentException { if (image_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project image coordinates to world coordinates\n" + "because the image coordinates point is null."); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (">>> Equirectangular_Projection.to_World: " + image_coordinate); Point2D.Double world_coordinate = null; if (Not_Identity) { if ((DEBUG & DEBUG_CONVERTERS) != 0) { double value; value = Center_Longitude + Sample_to_Projection_X (image_coordinate.getX ()) / Coefficient_CLR; System.out.println (" StrictMath.toDegrees (\n" +" Center_Longitude " + Center_Longitude + " +\n" +" Sample_to_Projection_X (" + image_coordinate.getX () + ") " + Sample_to_Projection_X (image_coordinate.getX ()) + " /\n" +" Coefficient_CLR " + Coefficient_CLR + " = " + value + "\n" +" ) = " + StrictMath.toDegrees (value)); value = Line_to_Projection_Y (image_coordinate.getY ()) / Center_Latitude_Radius; System.out.println (" StrictMath.toDegrees (\n" +" Line_to_Projection_Y (" + image_coordinate.getY () + ") " + Line_to_Projection_Y (image_coordinate.getY ()) + " /\n" +" Center_Latitude_Radius " + Center_Latitude_Radius + " = " + value + "\n" +" ) = " + StrictMath.toDegrees (value)); } world_coordinate = new Point2D.Double ( StrictMath.toDegrees ( Center_Longitude + Sample_to_Projection_X (image_coordinate.getX ()) / Coefficient_CLR), StrictMath.toDegrees ( Line_to_Projection_Y (image_coordinate.getY ()) / Center_Latitude_Radius) ); } else world_coordinate = new Point2D.Double (image_coordinate.getX (), image_coordinate.getY ()); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println ("<< Equirectangular_Projection.to_World: " + world_coordinate); return world_coordinate; } /** Get the image sample,line coordinate for a world longitude,latitude coordinate.

The conversion algorithm is:

@param world_coordinate The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees. @return The image sample,line coordinate. */ public Point to_Image ( Point2D world_coordinate ) throws IllegalArgumentException { if (world_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project world coordinates to image coordinates\n" + "because the world coordinates point is null."); Point image_coordinate = null; if (Not_Identity) image_coordinate = new Point ( (int)(Projection_X_to_Sample (Coefficient_CLR * (StrictMath.toRadians (world_coordinate.getX ()) - Center_Longitude)) + 0.5), // Round to nearest pixel. (int)(Projection_Y_to_Line (Center_Latitude_Radius * StrictMath.toRadians (world_coordinate.getY ())) + 0.5) // Round to nearest pixel. ); else image_coordinate = new Point ((int)world_coordinate.getX (), (int)world_coordinate.getY ()); return image_coordinate; } } pirl-2.3.8/PIRL/Image_Tools/Projection.java0000644000175000017500000010567512023520230020276 0ustar mathieumathieu/* Projection PIRL CVS ID: Projection.java,v 1.8 2012/09/11 02:23:20 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import PIRL.Strings.Words; import java.util.NoSuchElementException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.UnsupportedOperationException; import java.awt.Point; import java.awt.geom.Point2D; // For main. import PIRL.PVL.Parser; import PIRL.Utilities.Streams; /** A Projection is used to algorithmically map, or "project", from one image coordinate system to another while maintaining the spatial relationships of all coordinates.

Spatial projections are used in many contexts. The typical example is when a camera image of curved surface is to be mapped as if the surface were flat. The commonly known Mercator projection of a map of the Earth is such a projection. There are many map projections. See Snyder, John P., "Map Projections - A Working Manual", U.S Geological Survey Professional Paper 1395, 1987 for a discussion of the map projection algorithm used in these classes.

The core projection characteristic is an algorithm that maps coordinates from one coordinate system to another. The specific projection algorithms used - the implementation of a set of equations - will, of course, vary from one type of projection to another to achieve the desired mapping effect. In addition, the choice of projection is likey to depend on the range of coordinate values to be mapped - projections are likely to have increasing visual distortion effects as boundary conditions are approached - or the accuracy to be achieved - the implementing equations are like to make various simplifying assumptions. Nevertheless, all projections are expected to map coordinates between image and world coordinate systems. The former is in image pixel units of sample - horizontal distance from the left-most pixel - and and line - vertical distance from the top-most line - of a rectangular array of pixels. The latter is typically in planetary longitude - angular angular distance about the center of the planet relative to a zero-reference longitude - and latitude - angular angular distance about the center of the planet relative to the equator - usually measured in decimal degrees. However, other "world" coordinate systems are quite possible. In order to accommodate images of arbitrary location, an intermediate projeciton coordinate system is typically used by the projection equations, with a simple translational equation used to map between the projection coordinate system and the location of the image raster in that system.

A specific projection, implemented as a subclass of Projection, will require various parameters need by the projection equations. These are supplied by a PVL Parameter Aggregate. Because this Projection implementation assumes that planetary projections are to be implemented by subclasses, the Parameter group used to construct a Projection collects the common set of parameter values required by the specific projection algorithms. If all the required parameters are not provided the default identity projection - in which coordinate values are not changed - is used.

@author Bradford Castalia UA/PIRL @version 1.8 */ public class Projection { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Projection (1.8 2012/09/11 02:23:20)"; //! The projection name (override in subclass). private static final String PROJECTION_NAME = "Idenitity"; public static final String PROJECTION_PARAMETER_NAME = "MAP_PROJECTION_TYPE", EQUIRECTANGULAR_PROJECTION_NAME = "EQUIRECTANGULAR", POLAR_STEREOGRAPHIC_PROJECTION_NAME = "POLAR STEREOGRAPHIC", POLARSTEREOGRAPHIC_PROJECTION_NAME = "POLARSTEREOGRAPHIC", PROJECTION_LATITUDE_PARAMETER_NAME = "PROJECTION_LATITUDE_TYPE", PLANETOCENTRIC_PROJECTION_NAME = "PLANETOCENTRIC", EQUITORIAL_RADIUS_PARAMETER_NAME = "A_AXIS_RADIUS", POLAR_RADIUS_PARAMETER_NAME = "C_AXIS_RADIUS", METERS_PER_PIXEL_PARAMETER_NAME = "MAP_SCALE", POSITIVE_LONGITUDE_PARAMETER_NAME = "POSITIVE_LONGITUDE_DIRECTION", POSITIVE_LONGITUDE_EAST_NAME = "EAST", POSITIVE_LONGITUDE_WEST_NAME = "WEST", CENTER_LATITUDE_PARAMETER_NAME = "CENTER_LATITUDE", CENTER_LONGITUDE_PARAMETER_NAME = "CENTER_LONGITUDE", SAMPLE_OFFSET_PARAMETER_NAME = "SAMPLE_PROJECTION_OFFSET", LINE_OFFSET_PARAMETER_NAME = "LINE_PROJECTION_OFFSET", NOT_APPLICABLE_CONSTANT_PARAMETER_NAME = "NOT_APPLICABLE_CONSTANT"; protected boolean Not_Identity = false, Positive_West, // POSITIVE_LONGITUDE_DIRECTION Planetocentric; // PROJECTION_LATITUDE_TYPE public static final double DEFAULT_NA = -9998.0; protected double Meters_per_Pixel = 0.0, // MAP_SCALE Equitorial_Radius = 0.0, // A_AXIS_RADIUS Polar_Radius = 0.0, // C_AXIS_RADIUS NA = DEFAULT_NA; // NOT_APPLICABLE_CONSTANT /** Projection planetographic center latitude in radians. */ protected double Center_Latitude = 0.0; // CENTER_LATITUDE /** Projection center longitude in radians. */ protected double Center_Longitude = 0.0; // CENTER_LONGITUDE protected double Sample_Offset = 0.0, // SAMPLE_PROJECTION_OFFSET Line_Offset = 0.0; // LINE_PROJECTION_OFFSET // Derived values. protected double Eccentricity; public static final double PI_OVER_2 = StrictMath.PI * 0.5, PI_OVER_4 = StrictMath.PI * 0.25, DBL_EPSILON = 2.2204460492503131E-16; public static final boolean DEFAULT_USE_SPHERICAL = false; private static boolean Use_Spherical = DEFAULT_USE_SPHERICAL; private double Coefficient_PC_to_PG, Coefficient_PG_to_PC; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_CONVERTERS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Projection from a Parameter Aggregate.

@param parameters The Parameter Aggregate containing (at least) the minimally required parameter values. If all required parameters are not provided - including if the argument is null - the {@link #Is_Identity() identity projection flag} is set. @throws PVL_Exception If there is a problem reading the parameters or a parameter is found to have an invalid value. */ public Projection ( Parameter parameters ) throws PVL_Exception { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Projection: " + parameters); if (parameters == null) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Projection"); return; } try {NA = Find_Parameter (parameters, NOT_APPLICABLE_CONSTANT_PARAMETER_NAME) .Value ().double_Data ();} catch (Exception exception) {/* Use the default */} try { Meters_per_Pixel = Find_Parameter (parameters, METERS_PER_PIXEL_PARAMETER_NAME) .Value ().double_Data (); if (Meters_per_Pixel <= 0.0 || Meters_per_Pixel == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + METERS_PER_PIXEL_PARAMETER_NAME + " value: " + Meters_per_Pixel + '\n' + "The value must be a valid non-zero positive."); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Meters_per_Pixel = " + Meters_per_Pixel); Equitorial_Radius = Find_Parameter (parameters, EQUITORIAL_RADIUS_PARAMETER_NAME) .Value ().double_Data () * 1000.0; if (Equitorial_Radius <= 0.0 || Equitorial_Radius == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + EQUITORIAL_RADIUS_PARAMETER_NAME + " value: " + Equitorial_Radius + '\n' + "The value must be a valid non-zero positive."); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Equitorial_Radius = " + Equitorial_Radius); Polar_Radius = Find_Parameter (parameters, POLAR_RADIUS_PARAMETER_NAME) .Value ().double_Data () * 1000.0; if (Polar_Radius <= 0.0 || Polar_Radius == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + POLAR_RADIUS_PARAMETER_NAME + " value: " + Polar_Radius + '\n' + "The value must be a valid non-zero positive."); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Polar_Radius = " + Polar_Radius); // Planetocentric_to_Planetographic conversion coefficient. Coefficient_PC_to_PG = (Equitorial_Radius / Polar_Radius) * (Equitorial_Radius / Polar_Radius); // Planetographic_to_Planetocentric conversion coefficient. Coefficient_PG_to_PC = (Polar_Radius / Equitorial_Radius) * (Polar_Radius / Equitorial_Radius); Eccentricity = Eccentricity (Polar_Radius, Equitorial_Radius); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Eccentricity = " + Eccentricity); String string = Find_Parameter (parameters, POSITIVE_LONGITUDE_PARAMETER_NAME) .Value ().String_Data (); if (string.equalsIgnoreCase (POSITIVE_LONGITUDE_WEST_NAME)) Positive_West = true; else if (string.equalsIgnoreCase (POSITIVE_LONGITUDE_EAST_NAME)) Positive_West = false; else throw new PVL_Exception (ID + '\n' + "Invalid " + POSITIVE_LONGITUDE_PARAMETER_NAME + " value: " + string + '\n' + "The value must be \"" + POSITIVE_LONGITUDE_WEST_NAME + "\" or \"" + POSITIVE_LONGITUDE_EAST_NAME + "\"."); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Positive_West = " + Positive_West); Sample_Offset = Find_Parameter (parameters, SAMPLE_OFFSET_PARAMETER_NAME) .Value ().double_Data (); if (Sample_Offset == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + SAMPLE_OFFSET_PARAMETER_NAME + " value: " + Sample_Offset); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Sample_Offset = " + Sample_Offset); Line_Offset = Find_Parameter (parameters, LINE_OFFSET_PARAMETER_NAME) .Value ().double_Data (); if (Line_Offset == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + LINE_OFFSET_PARAMETER_NAME + " value: " + Line_Offset); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Line_Offset = " + Line_Offset); try {Planetocentric = Find_Parameter (parameters, PROJECTION_LATITUDE_PARAMETER_NAME) .Value ().String_Data () .equalsIgnoreCase (PLANETOCENTRIC_PROJECTION_NAME);} catch (NoSuchElementException exception) {/* false */} if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Planetocentric = " + Planetocentric); Center_Longitude = Find_Parameter (parameters, CENTER_LONGITUDE_PARAMETER_NAME) .Value ().double_Data (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Center_Longitude = " + Center_Longitude); if (Center_Longitude == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + CENTER_LONGITUDE_PARAMETER_NAME + " value: " + Center_Longitude); Center_Longitude = StrictMath.toRadians (Center_Longitude); if (Positive_West) Center_Longitude *= -1.0; if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" " + Center_Longitude); Center_Latitude = Find_Parameter (parameters, CENTER_LATITUDE_PARAMETER_NAME) .Value ().double_Data (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Center_Latitude = " + Center_Latitude); if (Center_Latitude == NA) throw new PVL_Exception (ID + '\n' + "Invalid " + CENTER_LATITUDE_PARAMETER_NAME + " value: " + Center_Latitude); Center_Latitude = StrictMath.toRadians (Center_Latitude); if (Planetocentric) Center_Latitude = Planetocentric_to_Planetographic (Center_Latitude); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" " + Center_Latitude); Not_Identity = true; } catch (NoSuchElementException exception) {/* Take this exception to mean that the image is not projected. */} if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Projection"); } /** Constructs a default identity Projection.

The {@link #Is_Identity() identity projection flag} is set. */ public Projection () {} /** Creates a Projection object that uses an appropriate specific Projection subclass projection implementation.

The choice of specific projection implementation is based on the value of the Parameter having the {@link #PROJECTION_PARAMETER_NAME}. This name is used to assemble a {@link #Projection_Class_Name(String) projection specific class name} to be loaded. Once loaded, a new instance is constructed using the parameters argument.

As a special case, if the projection name is {@link #POLAR_STEREOGRAPHIC_PROJECTION_NAME} the "_Spherical" suffix is appended if {@link #Use_Spherical(boolean)} is enabled so the faster but slightly less accurate spherical form of the projection will be used; otherwise the "_Elliptical" suffix is appended so the elliptical form will be used. Also, if the name is {@link #POLARSTEREOGRAPHIC_PROJECTION_NAME} it will be converted to the {@link #POLAR_STEREOGRAPHIC_PROJECTION_NAME}

@param parameters The Parameter Aggregate containing the projection definition parameter values. These parameters will be passed to the specific projection class to be constructed. If null, not an Aggregate Parameter, or there is no {@link #PROJECTION_PARAMETER_NAME} parameter a default Projection is constructed which will have the {@link #Is_Identity() identity projection flag} set. @return A Projection or Projection subclass, Object. @throws PVL_Exception If there is a problem reading the parameters or a parameter is found to have an invalid value. @throws NoSuchElementException If any required parameter was missing from the provided parameters. @throws UnsupportedOperationException If the expected Projection subclass could not be loaded or instantiated. @throws Throwable If the constructor of the loaded projection class throws an unexpected exception. @see Equirectangular_Projection @see Polar_Stereographic_Elliptical_Projection @see Polar_Stereographic_Spherical_Projection */ public static Projection Create ( Parameter parameters ) throws PVL_Exception, NoSuchElementException, UnsupportedOperationException, Throwable { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Projection.Create: " + parameters); Projection projection = null; if (parameters == null || ! parameters.Is_Aggregate ()) // Identity Projection when no parameters. projection = new Projection (); String projection_name = null; try { Parameter projection_parameter = Find_Parameter (parameters, PROJECTION_PARAMETER_NAME); try {projection_name = projection_parameter.Value ().String_Data ();} catch (PVL_Exception except) {} } catch (NoSuchElementException exception) {} if (projection_name == null) // Identity Projection when no valid projection name parameter. projection = new Projection (); else { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" " + PROJECTION_PARAMETER_NAME + " = " + projection_name); if (projection_name.equalsIgnoreCase (POLAR_STEREOGRAPHIC_PROJECTION_NAME) || projection_name.equalsIgnoreCase (POLARSTEREOGRAPHIC_PROJECTION_NAME)) { if (Use_Spherical) projection_name += "_spherical"; else projection_name += "_elliptical"; } projection_name = Projection_Class_Name (projection_name); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Projection class name: " + projection_name); Class projection_class = null; try {projection_class = Class.forName (projection_name);} catch (ClassNotFoundException exception) { try {projection_class = Class.forName ("PIRL.Image_Tools." + projection_name);} catch (ClassNotFoundException except) { throw new UnsupportedOperationException (ID + '\n' + "Unable to find the \"" + projection_name + "\" class."); } } try { Class[] parameter_class = new Class[1]; parameter_class[0] = parameters.getClass (); Constructor projection_constructor = projection_class.getConstructor (parameter_class); Object argument[] = new Object[1]; argument[0] = parameters; projection = (Projection)projection_constructor.newInstance (argument); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Projection: " + projection); } catch (InvocationTargetException exception) { Throwable throwable = exception.getCause (); if (throwable != null) throw throwable; throw exception; } catch (NoSuchElementException exception) {throw exception;} catch (Exception exception) { UnsupportedOperationException unsupported = new UnsupportedOperationException (ID + '\n' + "Unable to load the \"" + projection_name + "\" projection class."); unsupported.initCause (exception); } } if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Projection.Create: " + parameters); return projection; } /*============================================================================== Accessors */ public String Name () {return PROJECTION_NAME;} public boolean Is_Identity () {return ! Not_Identity;} public double Resolution () {return Meters_per_Pixel;} public double Equitorial_Radius () {return Equitorial_Radius;} public double Polar_Radius () {return Polar_Radius;} public double Eccentricity () {return Eccentricity;} public double Sample_Offset () {return Sample_Offset;} public double Line_Offset () {return Line_Offset;} public boolean Positive_West () {return Positive_West;} public Projection Positive_West ( boolean positive_west ) {Positive_West = positive_west; return this;} public boolean Planetocentric () {return Planetocentric;} public Projection Planetocentric ( boolean planetocentric ) {Planetocentric = planetocentric; return this;} public double Center_Latitude () {return Center_Latitude;} public double Center_Longitude () {return Center_Longitude;} public static boolean Use_Spherical ( boolean enabled ) { boolean previous_state = Use_Spherical; Use_Spherical = enabled; return previous_state; } /*============================================================================== Converters */ /** Get the world longitude,latitude coordinate for an image sample.line coordinate.

This method simply returns a copy of the coordinate argument. A projection specific subclass will override this method.

@param image_coordinate The image sample,line coordinate. @return A copy of the image_coordinate. */ public Point2D.Double to_World ( Point2D image_coordinate ) { if (image_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project world coordinates to image coordinates\n" + "because the world coordinates point is null."); return new Point2D.Double (image_coordinate.getX (), image_coordinate.getY ()); } /** Get the image sample,line coordinate for a world longitude,latitude coordinate.

This method simply returns a copy of the coordinate argument. A projection specific subclass will override this method.

@param world_coordinate The world longitude,latitude coordinate. @return A copy of the world_coordinate. */ public Point to_Image ( Point2D world_coordinate ) { if (world_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project image coordinates to world coordinates\n" + "because the image coordinates point is null."); return new Point ((int)world_coordinate.getX (), (int)world_coordinate.getY ()); } /** Converts an angle in decimal degrees, to a degrees, minutes, seconds representation.

The format of the degrees, minutes, seconds String representation is:

DDDd MMm SS.SSSs
where DDD is integer degrees, MM is integer minutes in the range 0 to 59, and SS.SSS is seconds with fractional seconds in the range 0.0 to 59.999 For example, 206.291 degrees is represented as "206d 17m 27.600s". N.B.: A fixed field format is used in which leading spaces and trailing zeros are used to each value occurs at a specific location in the String representation.

@param angle The angle in degrees to be represented. @return The String representation. */ public static String Degrees_Minutes_Seconds ( double angle ) { int degrees = (int)angle; double fraction = Math.abs (angle - degrees) * 60.0; int minutes = (int)fraction; fraction = (fraction - minutes) * 60.0; int seconds = (int)fraction; int msecs = (int)((fraction - seconds) * 1000.0 + 0.5); if (msecs >= 1000) { msecs -= 1000; seconds++; } if (seconds >= 60) { seconds -= 60; minutes++; } if (minutes >= 60) { minutes -= 60; degrees++; } return ((degrees < 10) ? " " : ((degrees < 100) ? " " : "")) + degrees + "d " + ((minutes < 10) ? " " : "") + minutes + "m " + ((seconds < 10) ? " " : "") + seconds + "." + ((msecs < 10) ? "00" : ((msecs < 100) ? "0" : "")) + msecs + "s"; } /** Converts an angle in decimal degrees, to an hours, minutes, seconds representation.

The format of the hours, minutes, seconds String representation is:

HHh MMm SS.SSSs
where HH is integer hours in the range 0 to 24, MM is integer minutes in the range 0 to 59, and SS.SSS is seconds with fractional seconds in the range 0.0 to 59.999 For example, 206.291 degrees is represented as "13h 45m 9.840s". N.B.: A fixed field format is used in which leading spaces and trailing zeros are used to each value occurs at a specific location in the String representation.

@param angle The angle in degrees to be represented. @return The String representation. */ public static String Hours_Minutes_Seconds ( double angle ) { angle = To_360 (angle) / 15.0; int hours = (int)angle; double fraction = Math.abs (angle - hours) * 60.0; int minutes = (int)fraction; fraction = (fraction - minutes) * 60.0; int seconds = (int)fraction; int msecs = (int)((fraction - seconds) * 1000.0 + 0.5); if (msecs >= 1000) { msecs -= 1000; seconds++; } if (seconds >= 60) { seconds -= 60; minutes++; } if (minutes >= 60) { minutes -= 60; hours++; } return ((hours < 10) ? " " : "") + hours + "h " + ((minutes < 10) ? " " : "") + minutes + "m " + ((seconds < 10) ? " " : "") + seconds + "." + ((msecs < 10) ? "00" : ((msecs < 100) ? "0" : "")) + msecs + "s"; } public static double Decimal_Degrees ( String parts ) throws IllegalArgumentException, NumberFormatException { double degrees; Words words = new Words (parts); String part = words.Next_Word (); int index = part.length () - 1; if (index < 0) throw new IllegalArgumentException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + '"'); char character = part.charAt (index); if (character == 'd' || character == 'D') { // Degrees. try {degrees = Integer.parseInt (part.substring (0, index));} catch (NumberFormatException exception) { throw new NumberFormatException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Degrees (the first value) must be an integer."); } } else { // Hours. if (character == 'h' || character == 'H') part = part.substring (0, index); try {degrees = Integer.parseInt (part) * 15;} // Convert to degrees. catch (NumberFormatException exception) { throw new NumberFormatException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Hours (the first value) must be an integer."); } } degrees = To_360 (degrees); part = words.Next_Word (); if ((index = part.length () - 1) >= 0) { // Minutes. character = part.charAt (index); if (character == 'm' || character == 'M') part = part.substring (0, index); int minutes; try {minutes = Integer.parseInt (part);} catch (NumberFormatException exception) { throw new NumberFormatException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Minutes (the second value) must be an integer."); } if (minutes < 0 || minutes > 59) throw new IllegalArgumentException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Minutes (the second value) must be in the range [0-60)."); degrees += (double)minutes * (15.0 / 60.0); // Convert to degrees. part = words.Next_Word (); if ((index = part.length () - 1) >= 0) { // Seconds. character = part.charAt (index); if (character == 's' || character == 'S') part = part.substring (0, index); double seconds; try {seconds = Double.parseDouble (part);} catch (NumberFormatException exception) { throw new NumberFormatException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Seconds (the third value) must be a real number."); } if (seconds < 0.0 || seconds >= 60.0) throw new IllegalArgumentException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Seconds (the third value) must be in the range [0-60)."); degrees += (double)seconds * (15.0 / 3600.0); // Convert to degrees. part = words.Next_Word (); if (part.length () != 0) throw new IllegalArgumentException (ID + '\n' + "Unable to convert to decimal degrees: \"" + parts + "\"\n" + "Seconds (the third value) should be the last part."); } } return degrees; } /** Convert planetocentric latitude to planetographic latitude.

@param latitude The latitude, in radians, to convert. @return The converted latitude value. */ public double Planetocentric_to_Planetographic ( double latitude ) { if (Not_Identity && Math.abs (latitude) < PI_OVER_2) latitude = StrictMath.atan (StrictMath.tan (latitude) * Coefficient_PC_to_PG); return latitude; } /** Convert planetographic latitude to planetocentric latitude.

@param latitude The latitude, in radians, to convert. @return The converted latitude value. */ public double Planetographic_to_Planetocentric ( double latitude ) { if (Not_Identity && Math.abs (latitude) < PI_OVER_2) latitude = StrictMath.atan (StrictMath.tan (latitude) * Coefficient_PG_to_PC); return latitude; } protected double Projection_X_to_Sample ( double projection_x ) {return (projection_x / Meters_per_Pixel) + Sample_Offset;} protected double Projection_Y_to_Line ( double projection_y ) {return (projection_y / Meters_per_Pixel) - Line_Offset;} protected double Sample_to_Projection_X ( double sample ) {return (sample - Sample_Offset) * Meters_per_Pixel;} protected double Line_to_Projection_Y ( double line ) {return (-Line_Offset - line) * Meters_per_Pixel;} /*============================================================================== Derived values */ /** Calculate the planet eccentricity.

E = sqrt (1 - polar_radius**2 / equitorial_radius**2)

@param equitorial_radius The planet equitorial radius. @param polar_radius The planet polar radius. @return The planet eccentricity. */ public static double Eccentricity ( double polar_radius, double equitorial_radius ) { return StrictMath.sqrt (1.0 - ((polar_radius * polar_radius) / (equitorial_radius * equitorial_radius))); } /** Calculate the radius of the planet at some latitude.

R = Re * Rp / sqrt (a**2 + b**2)
where:

Re = {@link Projection#Equitorial_Radius() Equitorial_Radius} Rp = {@link Projection#Polar_Radius() Polar_Radius} a = Re * sin (latitude)
b = Rp * cos (latitude)

@param latitude The latitude, in radians, at which the local radius is to be calculated. @return The radius of the planet at the specified latitude. */ public double Local_Radius ( double latitude ) { double radius = 0.0; if (Not_Identity) { if (latitude == PI_OVER_2) radius = Polar_Radius; else if (latitude == 0.0) radius = Equitorial_Radius; else { double coefficient_e = Equitorial_Radius * StrictMath.sin (latitude), coefficient_p = Polar_Radius * StrictMath.cos (latitude); radius = Equitorial_Radius * Polar_Radius / StrictMath.sqrt ((coefficient_p * coefficient_p) + (coefficient_e * coefficient_e)); } } return radius; } /** Ensure that a longitude value is in the range 0 to 360 degrees.

A negative longitude value is repeatedly increased by 360 until it is no longer negative. A longitude value greater than or equal to 360 is repeatedly decreased by 360 until it is less than 360.

@param longitude The longitude value to map to the 0 to 360 degree domain. @return The longitude value in the 0-360 degree domain. */ public static double To_360 ( double longitude ) { while (longitude < 0.0) longitude += 360.0; while (longitude >= 360.0) longitude -= 360.0; return longitude; } /** Ensure that a longitude value is in the range -180 to 180 degrees.

A longitude value less than -180 is repeatedly increased by 360 until it is greater than or equal to -180. A longitude value greater than or equal to 180 is repeatedly decreased by 360 until it is less than 180.

@param longitude The longitude value to map to the -180 to 180 degree domain. @return The longitude value in the -180 to 180 degree domain. */ public static double To_180 ( double longitude ) { while (longitude < -180.0) longitude += 360.0; while (longitude >= 180.0) longitude -= 360.0; return longitude; } /*============================================================================== Helpers */ /** Produce a class name for a projection name.

The projection name is first trimmed of leading and trailing whitespace and all characters are set to lowercase. If the result starts with "polar" but is not followed by a space (' ') or underbar ('_') character an underbar is inserted following the "polar" prefix. Then any remaining space characters are changed to '_' characters. The first character and every character following an underbar character is set to uppercase. Finally the "_Projection" String is appended.

@param projection_name The projection name String. @return The class name String. */ public static String Projection_Class_Name ( String projection_name ) { if (projection_name == null) return null; StringBuffer class_name = new StringBuffer (projection_name = projection_name.trim ().toLowerCase ()); // Special case. if (projection_name.startsWith ("polar") && class_name.length () > 5 && class_name.charAt (5) != ' ' && class_name.charAt (5) != '_') class_name.insert (5, "_"); if (class_name.length () != 0) { class_name.setCharAt (0, Character.toUpperCase (class_name.charAt (0))); int index = 0; while ((index = class_name.indexOf (" ", index)) > 0) class_name.setCharAt (index++, '_'); index = 0; while ((index = class_name.indexOf ("_", index) + 1) > 0) class_name.setCharAt (index, Character.toUpperCase (class_name.charAt (index))); class_name.append ("_Projection"); } return class_name.toString (); } /** Find a named parameter in PDS label parameters. @param label The PDS label parameters. @param name The name of the parameter to find. @return The Parameter that was found. @throws NoSuchElementException If the parameter was not found. */ protected static Parameter Find_Parameter ( Parameter label, String name ) throws NoSuchElementException { Parameter parameter = label.Find (name); if (parameter == null) throw new NoSuchElementException (ID + '\n' + "The required \"" + name + "\" parameter was not found in the PDS label."); return parameter; } /*============================================================================== main */ public static void main ( String[] args ) { System.out.println (ID); if (args.length == 0) Usage (); Parameter parameters = null; boolean image_coordinates = false, spherical = false; Point2D.Double coordinate = null; int index; String number; for (int count = 0; count < args.length; count++) { if (args[count].length () == 0) continue; if (args[count].charAt (0) == '-' && args[count].length () > 1) { switch (args[count].charAt (1)) { case 'A': case 'a': if ((count + 1) == args.length) Usage (); double angle = 0; try {angle = Double.parseDouble (args[++count]);} catch (NumberFormatException exception) { try {angle = Decimal_Degrees (args[count]);} catch (Exception except) { System.out.println ("Not a valid angle: " + args[count] + '\n' + except.getMessage ()); Usage (); } System.out.println ("Angle = " + args[count]); break; } System.out.println ("Angle = " + angle + '\n' +" " + Projection.Hours_Minutes_Seconds (angle) + '\n' +" " + Projection.Degrees_Minutes_Seconds (angle)); break; case 'S': case 's': spherical = true; break; case 'I': case 'i': image_coordinates = true; case 'W': case 'w': if ((count + 1) == args.length || coordinate != null) Usage (); coordinate = Coordinate (args[++count]); break; default: System.out.println ("Unknown option: " + args[count]); Usage (); } } else { if (parameters != null) Usage (); try {parameters = new Parameter (new Parser (Streams.Get_Stream (args[count])));} catch (PVL_Exception exception) { System.out.println ("Unable to read PVL source: " + args[count] + '\n' + exception.getMessage ()); System.exit (2); } } } if (parameters != null || coordinate != null) { if (parameters == null || coordinate == null) Usage (); try { Projection.Use_Spherical (spherical); Projection projection = Projection.Create (parameters); if (image_coordinates) System.out.println ("Image coordinate: " + coordinate + '\n' +"World coordinate: " + projection.to_World (coordinate)); else System.out.println ("World coordinate: " + coordinate + '\n' +"Image coordinate: " + projection.to_Image (coordinate)); } catch (Throwable exception) { exception.printStackTrace (); System.exit (3); } } System.exit (0); } private static Point2D.Double Coordinate ( String coordinate ) { int index = coordinate.indexOf (','); if (index < 0) Usage (); Point2D.Double point = null; try { point = new Point2D.Double ( Double.parseDouble (coordinate.substring (0, index)), Double.parseDouble (coordinate.substring (index + 1)) ); } catch (NumberFormatException exception) { System.out.println ("Not a valid coordinate: " + coordinate); Usage (); } return point; } public static void Usage () { System.out.println ("Usage: Projection" + " [-Angle " + " -Image x,y | -World lon,lat"); System.exit (1); } } pirl-2.3.8/PIRL/Image_Tools/Polar_Stereographic_Elliptical_Projection.java0000644000175000017500000002360712030434421026452 0ustar mathieumathieu/* Polar_Stereographic_Elliptical_Projection PIRL CVS ID: Polar_Stereographic_Elliptical_Projection.java,v 1.4 2012/09/25 23:06:57 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Image_Tools; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import java.awt.Point; import java.awt.geom.Point2D; public class Polar_Stereographic_Elliptical_Projection extends Projection { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Polar_Stereographic_Elliptical_Projection (1.4 2012/09/25 23:06:57)"; private static final String PROJECTION_NAME = "Polar Stereographic Elliptical"; private double E_over_2, Center_Latitude_Sign, Distance_Coefficient; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_CONVERTERS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Polar_Stereographic_Elliptical_Projection ( Parameter parameters ) throws PVL_Exception { // Initialize the base projection parameters. super (parameters); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Polar_Stereographic_Elliptical_Projection: " + parameters); if (Not_Identity) { // Precalculated coefficients. E_over_2 = Eccentricity * 0.5; if (Center_Latitude < 0.0) Center_Latitude_Sign = -1.0; else Center_Latitude_Sign = 1.0; boolean at_pole = true; double coefficient_t = 0.0; if ((PI_OVER_2 - Math.abs (Center_Latitude)) > DBL_EPSILON) { coefficient_t = Coefficient_T (Center_Latitude); if (Math.abs (coefficient_t) >= DBL_EPSILON) at_pole = false; } if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" at_pole = " + at_pole); // For to_World and to_Image conversion. if (at_pole) Distance_Coefficient = StrictMath.sqrt (StrictMath.pow (1.0 + Eccentricity, 1.0 + Eccentricity) * StrictMath.pow (1.0 - Eccentricity, 1.0 - Eccentricity)) / (2.0 * Equitorial_Radius); else { double phi = Center_Latitude_Sign * Center_Latitude, es = Eccentricity * StrictMath.sin (phi); Distance_Coefficient = coefficient_t / (Equitorial_Radius * StrictMath.cos (phi) / StrictMath.sqrt (1.0 - es * es)); } } if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Polar_Stereographic_Elliptical_Projection"); } /*============================================================================== Accessors */ public String Name () {return PROJECTION_NAME;} /*============================================================================== Converters */ /** Get the world longitude,latitude coordinate for an image sample,line coordinate.

The conversion algorithm is:

@param image_coordinate The image sample,line coordinate. @return The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees within the range 0-360. */ public Point2D.Double to_World ( Point2D image_coordinate ) throws IllegalArgumentException, ArithmeticException { if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (">>> Polar_Stereographic_Elliptical_Projection.to_World: " + image_coordinate); if (image_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project image coordinates (" + (int)image_coordinate.getX () + ',' + (int)image_coordinate.getX () + ") to world coordinates\n" + "because the image coordinates point is null."); Point2D.Double world_coordinate = null; if (Not_Identity) { double x = Center_Latitude_Sign * Sample_to_Projection_X (image_coordinate.getX ()), y = Center_Latitude_Sign * Line_to_Projection_Y (image_coordinate.getY ()), distance = StrictMath.sqrt ((x * x) + (y * y)), coefficient_t = distance * Distance_Coefficient; // Latitude. double latitude = Center_Latitude_Sign * Coefficient_Phi2 (coefficient_t); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (" Center_Latitude_Sign = " + Center_Latitude_Sign + '\n' +" x = " + x + '\n' +" y = " + y + '\n' +" distance = " + distance + '\n' +" Distance_Coefficient = " + Distance_Coefficient + '\n' +" coefficient_t = " + coefficient_t + '\n' +" Coefficient_Phi2 = " + Coefficient_Phi2 (coefficient_t) + '\n' +" latitude = " + latitude); if (Math.abs (latitude) > PI_OVER_2) throw new ArithmeticException (ID + '\n' + "Projection of image coordinates (" + (int)image_coordinate.getX () + ',' + (int)image_coordinate.getX () + ") to world coordinates\n" + "resulted in an invalid latitude of " + Math.toDegrees (latitude) + " degrees."); if (Planetocentric) latitude = Planetographic_to_Planetocentric (latitude); latitude = StrictMath.toDegrees (latitude); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (" latitude = " + latitude); // Longitude. double longitude = ((distance == 0.0) ? (Center_Latitude_Sign * Center_Longitude) : (Center_Latitude_Sign * StrictMath.atan2 (x, -y) + Center_Longitude)); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println ('\n' +" Center_Longitude = " + Center_Longitude + '\n' + ((distance == 0.0) ? "" : (" StrictMath.atan2 (x, -y) = " + StrictMath.atan2 (x, -y) + '\n')) +" longitude = " + longitude); if (Positive_West) longitude *= -1.0; longitude = To_360 (StrictMath.toDegrees (longitude)); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (" longitude = " + longitude); world_coordinate = new Point2D.Double (longitude, latitude); } else world_coordinate = new Point2D.Double (image_coordinate.getX (), image_coordinate.getY ()); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println ("<<< Polar_Stereographic_Elliptical_Projection.to_World: " + world_coordinate); return world_coordinate; } /** Get the image sample,line coordinate for a world longitude,latitude coordinate.

The conversion algorithm is:

@param world_coordinate The world longitude,latitude coordinate. The {@link Point2D#getX() x} value of the coordinate Point2D is the longitude; the {@link Point2D#getY() y} value is the latitude. Values are in degrees. @return The image sample,line coordinate. */ public Point to_Image ( Point2D world_coordinate ) throws IllegalArgumentException { if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println (">>> Polar_Stereographic_Elliptical_Projection.to_Image: " + world_coordinate); if (world_coordinate == null) throw new IllegalArgumentException (ID + '\n' + "Unable to project world coordinates to image coordinates\n" + "because the world coordinates point is null."); Point image_coordinate = null; if (Not_Identity) { double longitude = StrictMath.toRadians (world_coordinate.getX ()), latitude = StrictMath.toRadians (world_coordinate.getY ()); if (Planetocentric) latitude = Planetocentric_to_Planetographic (latitude); if (Positive_West) longitude *= -1.0; double lambda = Center_Latitude_Sign * (longitude - Center_Longitude), distance = Coefficient_T (latitude) / Distance_Coefficient; image_coordinate = new Point ( (int)(Projection_X_to_Sample ( Center_Latitude_Sign * distance * StrictMath.sin (lambda)) + 0.5), // Round to nearest pixel. (int)(Projection_Y_to_Line ( -Center_Latitude_Sign * distance * StrictMath.cos (lambda)) + 0.5) // Round to nearest pixel. ); } else image_coordinate = new Point ((int)image_coordinate.getX (), (int)image_coordinate.getY ()); if ((DEBUG & DEBUG_CONVERTERS) != 0) System.out.println ("<<< Polar_Stereographic_Elliptical_Projection.to_Image: " + image_coordinate); return image_coordinate; } /*============================================================================== Derived values */ private double Coefficient_T ( double latitude ) { if ((PI_OVER_2 - Math.abs (latitude)) < DBL_EPSILON) return 0.0; latitude *= Center_Latitude_Sign; double e_sin_lat = Eccentricity * StrictMath.sin (latitude); return StrictMath.tan (0.5 * (PI_OVER_2 - latitude)) / StrictMath.pow ((1.0 - e_sin_lat) / (1.0 + e_sin_lat), E_over_2); } private double Coefficient_Phi2 ( double coefficient_t ) throws ArithmeticException { double phi = PI_OVER_2 - 2.0 * StrictMath.atan (coefficient_t), difference = Double.MAX_VALUE; int iteration; for (iteration = 0; iteration < 15 && difference > 0.0000000001; iteration++) { double eccentricity_sine_phi = Eccentricity * StrictMath.sin (phi), new_phi = PI_OVER_2 - 2.0 * StrictMath.atan (coefficient_t * StrictMath.pow ( (1.0 - eccentricity_sine_phi) / (1.0 + eccentricity_sine_phi), E_over_2 )); difference = Math.abs (new_phi - phi); phi = new_phi; } if (iteration == 15) throw new ArithmeticException (ID + '\n' + "Computation of coefficient phi2 failed to converge\n" + "using coefficient t of " + coefficient_t + '.'); return phi; } } pirl-2.3.8/PIRL/Image_Tools/package.html0000644000175000017500000000370511742733714017613 0ustar mathieumathieu The PIRL Image_Tools package contains tools for working with images.

The HTTP_ImageInputStream implements the ImageInputStream interface for a remotely accessed file via the HTTP protocol. Random access is provided by buffering source data using content ranging from the remote file. Automatic URL redirect and reconnect is supported.

A set of classes are provided for accessing JP2 image file format metadata boxes and its embedded JPEG2000 codestream segments descriptions. The information is provided as a PVL Parameter Aggregate.

A set of Projection classes are provided for mapping coordinates between image x,y and real world longitude,latitude values. The required map projection information is contained in a PVL Parameter Aggregate used to construct the Projection. A Projection factory method will examine the parameters to determine the specific subclass that implements the appropriate projection algorithms that is to be loaded and constructed. pirl-2.3.8/PIRL/Configuration/0000755000175000017500000000000012052546520015722 5ustar mathieumathieupirl-2.3.8/PIRL/Configuration/Makefile0000644000175000017500000000065310577661674017410 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.5 2007/03/20 04:31:56 castalia Exp # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm JPATH ?= ../..:$(JCM) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Configuration_Exception.class \ Configuration.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Configuration/Configuration.java0000644000175000017500000031073311742733240021405 0ustar mathieumathieu/* Configuration PIRL CVS ID: Configuration.java,v 1.55 2012/04/16 06:05:20 castalia Exp Copyright (C) 2003-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Configuration; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.Parser; import PIRL.PVL.Lister; import PIRL.PVL.Selector; import PIRL.PVL.Selection; import PIRL.PVL.PVL_Exception; import PIRL.Conductor.Reference_Resolver; import PIRL.Utilities.Host; import PIRL.Strings.String_Buffer; import java.lang.StringBuffer; import java.util.Vector; import java.util.Stack; import java.util.Hashtable; import java.util.Properties; import java.util.Enumeration; import java.util.ListIterator; import java.util.Iterator; import java.io.File; import java.io.FileInputStream; import java.io.StringWriter; import java.io.InputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.net.URL; import java.net.MalformedURLException; import java.text.ParseException; import java.util.regex.Pattern; /** A Configuration maintains a Parameter list used for configuration management.

A Configuration is constructed by parsing source text in Parameter Value Language (PVL) format (as read by a Parameter {@link PIRL.PVL.Parser Parser}), or from another Configuration or PVL Parameter. The {@link #Default_Source() Default_Source} will be used if no source is specified. Except when a Configuration is constructed as a copy of another Configuration (or Parameter Aggregate), the {@link #Defaults Defaults} Parameters are always included; if the special name {@link #DEFAULTS DEFAULTS} is specified, only the Defaults Parameters will be in the Configuration. In all cases, the Parameters in the Configuration will be {@link #Coalesce() Coalesce}d together.

The parameters of a Configuration are refered to by their pathname. The pathname may include a {@link Parameter#Path_Delimiter() delimiter} (usually the '/' character) separated sequence of names where each segment names a parameter group within which subsequent named parameters are nested. A pathname that begins with a delimiter is rooted at the top of the Configuration parameter groupings (i.e. these pathnames have an absolute location in the Configuration). Otherwise the pathname is rooted at the first occurance of a parameter having the first segment name (i.e. these pathnames have a relative location in the Configuration).

There are a set of parameter names that are known to the Configuration package:

{@link #CLASSPATH Classpath}
The Java CLASSPATH. The default is obtained from the "java.class.path" System Property.
{@link #HOST Host}
An application-specific hostname. The default is "localhost."
{@link #USER User}
A username to be supplied to the application. The default is obtained from the "user.name" System Property.
{@link #ALIAS Alias}
The name of the group from which a new Configuration was created when an alias (a linked parameter) was used rather than a group name.

In addition, the {@link #INCLUDE} parameter is recognized when {@link #Include(boolean) Include} mode is enabled as a parameter with a value that references another source of Parameters that is to be included in the Configuration replacing the Include parameter. Include files may be nested. Cyclical Include file (but not URL) chains are caught.

@see PIRL.PVL.Parameter @author Bradford Castalia - UA/PIRL @version 1.55 */ public class Configuration extends Parameter implements Cloneable { /** Class name and version identification. */ public static final String ID = "PIRL.Configuration.Configuration (1.55 2012/04/16 06:05:20)"; /** The name of the configuration source from which the parameters have been set.

This will be {@link #DEFAULTS DEFAULTS} if the Configuration was constructed soley from the {@link #Defaults Defaults} Parameters. It will be null if it was not constructed from the contents of a file (i.e. it was constructed from a Parameter). */ private String Config_Source = null; /** The parameter name for the Java CLASSPATH: "Classpath". */ public static final String CLASSPATH = "Classpath"; /** The parameter name for the application-specific hostname: "Host". */ public static final String HOST = "Host"; /** The parameter name for the application-specific username: "User". */ public static final String USER = "User"; /** The parameter name for a linked parameter used to create a new Configuration: "Alias".

@see #Group(String) */ public static final String ALIAS = "Alias"; /** The name of the Group containing the environment variable parameters: "/Environment".

@see #Add_Environment() */ public static final String ENVIRONMENT = Path_Delimiter () + "Environment"; /** The name of a parameter that refers to a file of parameters to be included at that point in the Configuration: "@Include". */ public static final String INCLUDE = "@Include"; /** The default automatic {@link #Include(boolean) Include} state. */ public static boolean Include_Default = true; private boolean Include_Includes = Include_Default; /** Parameter name prefix for the start of an {@link #Include(boolean) Include} file when tracing is enabled. */ public static final String INCLUDE_START = "INCLUDE_START"; /** Parameter name prefix for an {@link #CONTINUE_INCLUDE_ON_ERROR_FLAG ignored error} report from an {@link #Include(boolean) Include} file when tracing is enabled. */ public static final String INCLUDE_ERROR = "INCLUDE_ERROR"; /** Parameter name prefix for the end of an {@link #Include(boolean) Include} file when tracing is enabled. */ public static final String INCLUDE_END = "INCLUDE_END"; /** The default automatic {@link #Include(boolean) Include} tracing flag. */ public static boolean Include_Tracing_Default = false; private boolean Include_Tracing = Include_Tracing_Default; /** {@link #INCLUDE} parameter value flag that, when found at the beginning of the value, causes an error on the include - the source does not exists - to be ignored and the include skipped. */ public static final String CONTINUE_INCLUDE_ON_ERROR_FLAG = "-"; /** The name of the internal default parameters: "DEFAULTS". */ public static final String DEFAULTS = "DEFAULTS"; /** The name of the default configuration source: initially "DEFAULTS". */ public static String Default_Source = DEFAULTS; /** The default parameters conditionally set during {@link #Configure(InputStream) configuration}.

The default parameters are:

{@link #CLASSPATH CLASSPATH}
The "java.class.path" System Property.
{@link #USER USER}
The "user.name" System Property.
{@link #HOST HOST}
The "localhost" String.
*/ public static final Parameter Defaults = new Parameter (DEFAULTS); static { try { Defaults.Add (new Parameter (CLASSPATH) .Value (System.getProperty ("java.class.path"))); Defaults.Add (new Parameter (USER) .Value (System.getProperty ("user.name"))); Defaults.Add (new Parameter (HOST) .Value ("localhost")); } catch (PVL_Exception exception) {/* This shouldn't happen. */} } /** The default automatic {@link #Defaults(boolean) Defaults} state. */ public static boolean Defaults_Default = true; private boolean Include_Defaults = Defaults_Default; private static Class Relative_to_Class = null; /** The first duplicate parameter in the list gets preference when coalescing.

When a duplicate Assignment parameter pathname is found while {@link #Coalesce() coalescing} the Configuration, give preference to the first parameter; i.e. remove all but the first of any duplicate pathname parameters. */ public static final int PREFER_FIRST_PARAMETER = 1; /** The last duplicate parameter in the list gets preference when coalescing.

When a duplicate Assignment Parameter pathname is found while {@link #Coalesce() coalescing} the Configuration, give preference to the last parameter; i.e. remove all but the last of any duplicate pathname parameters. */ public static final int PREFER_LAST_PARAMETER = -1; /** Duplicate parameters in the list when coalescing causes an exception.

When a duplicate Assignment Parameter pathname is found while {@link #Coalesce() coalescing} the Configuration, a Configuration_Exception will be thrown. */ public static final int THROW_ON_DUPLICATE_PARAMETERS = 0; /** The default {@link #Duplicate_Parameter_Action()}. */ public static int Duplicate_Parameter_Action_Default = THROW_ON_DUPLICATE_PARAMETERS; private int Duplicate_Parameter_Action = Duplicate_Parameter_Action_Default; /** The default {@link #Case_Sensitive()} state. */ public static boolean Case_Sensitive_Default = false; /** Is parameter name matching case sensitive? */ private boolean Case_Sensitive = Case_Sensitive_Default; public static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_GET = 1 << 0, DEBUG_SET = 1 << 1, DEBUG_CONFIGURE = 1 << 2, DEBUG_INCLUDE = 1 << 3, DEBUG_UTILITY = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a default Configuration.

The {@link #Defaults} are used to set the initial parameters. {@link #DEFAULTS} is the name of this Configuration. */ public Configuration () { try {Configure (DEFAULTS);} catch (Configuration_Exception exception) {/* This shouldn't happen if the Defaults are OK. */} } /** Constructs a Configuration using the specified source.

@param source The source String reference for the Configuration PVL. This may be in the form of a URL representation. If null, the {@link #Default_Source() Default_Source} is used. @throws IllegalArgumentException If the source is not a URL and no file or system resource can be found with the source name. @throws Configuration_Exception If the source can not be read or successfully parsed for parameters, {@link #INCLUDE} files could not be included, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see #Configure(String) @see #Include(boolean) */ public Configuration ( String source ) throws Configuration_Exception {Configure (source);} /** Constructs a Configuration using the specified URL.

@param url A URL object to use as the source of Configuration PVL. @throws Configuration_Exception If a stream can not be opened from the URL, there was a problem while parsing the source for parameters, {@link #INCLUDE} files could not be included, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see #Configure(URL) @see #Include(boolean) */ public Configuration ( URL url ) throws Configuration_Exception {Configure (url);} /** Constructs a Configuration using the specified InputStream.

@param stream An InputStream object to use as the source of Configuration PVL. @throws Configuration_Exception If there was a problem while parsing the stream for parameters, {@link #INCLUDE} files could not be included, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see #Configure(InputStream) @see #Include(boolean) */ public Configuration ( InputStream stream ) throws Configuration_Exception {Configure (stream);} /** Constructs a Configuration from a Parameter.

If the Parameter is null or empty (has the {@link Parameter#UNKNOWN} classification) an empty Configuration with the name {@link Parser#CONTAINER_NAME} is constructed. If the Parameter is an Assignment it becomes the only parameter in a Configuration with the name {@link Parser#CONTAINER_NAME}. If the Parameter is an Aggregate, its Parameter list is copied into the new Configuration which is given the name of the Aggregate (the Configuration is a copy of the Parameter).

If automatic include is enabled any {@link #INCLUDE} parameters are processed. Finally, the results are {@link #Coalesce() Coalesce}d.

N.B.: Only the provided Parameter is included in the new Configuration; Defaults are not included.

@param parameter The Parameter which will be copied into this Configuration. @throws Configuration_Exception If the Parameter contents could not be added to the new Configuration, {@link #INCLUDE} files could not be included, or there was a problem coalescing the results (probably due to duplicate parameter names). @see #Coalesce() @see #Include(boolean) */ public Configuration ( Parameter parameter ) throws Configuration_Exception { if (parameter != null) { try { if (parameter.Is_Aggregate ()) { Data (parameter); Name (parameter.Name ()); } else { Add (new Parameter (parameter)); Name (Parser.CONTAINER_NAME); } if (Include_Includes) Include (); Coalesce (); } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Failed to construct a new Configuration from the \"" + parameter.Name () + "\" Parameter." + exception_message (exception), exception ); } } else Ensure_Aggregate_Configuration (); } /** Copies a Configuration.

This is the same as constructing a Configuration from a Parameter, followed by copying over the Configuration characteristics {@link #Case_Sensitive(boolean) case sensitivity}, {@link #Duplicate_Parameter_Action(int) duplicate parameter handling}, {@link #Include(boolean) Include processing}, {@link #Include_Tracing(boolean) Include tracing}, {@link #Defaults(boolean) Defaults parameters inclusion} and the {@link #Source() filename} if non-null;

N.B.: The state of this Configuration is used during the copy, not the state of the copied Configuration. For example, copied {@link #INCLUDE} parameters are processed if automatic {@link #Include(boolean) Include} is enabled for this Configuration; the automatic Include state of the Configuration being copied is not used.

@param configuration The Configuration to be copied. @throws Configuration_Exception If the Parameter contents could not be added to the new Configuration, or there was a problem coalescing the results (probably due to duplicate parameter names). @see #Configuration(Parameter) */ public Configuration ( Configuration configuration ) throws Configuration_Exception { this ((Parameter)configuration); Config_Source = configuration.Config_Source; Include_Includes = configuration.Include_Includes; Include_Tracing = configuration.Include_Tracing; Include_Defaults = configuration.Include_Defaults; Duplicate_Parameter_Action = configuration.Duplicate_Parameter_Action; Case_Sensitive = configuration.Case_Sensitive; } /** Clones a Configurtion.

@return An Object of class Configuration that is a copy of this Configuration. This will be null if there was a problem making the copy. @see #Configuration(Configuration) @see Object#clone() @see Cloneable */ public Object clone () { try {return new Configuration (this);} catch (Configuration_Exception exception) {return null;} } /*============================================================================== Accessors */ /** Gets the value(s) for a parameter at a pathname.

If specific is true then the values will be obtained from the {@link #Specific_Parameter(String) Specific_Parameter}, otherwise the {@link #Effective_Parameter(String) Effective_Parameter} will be used.

@param pathname A parameter pathname. @param specific true if only a parameter at the specific pathname is acceptable, otherwise default values from an effective parameter will do. @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. */ public Vector Get ( String pathname, boolean specific ) {return Get (Parameter (pathname, specific));} /** Gets the value(s) for a parameter at the Nth pathname.

@param pathname A parameter pathname. @param specific true if only a parameter at the specific pathname is acceptable, otherwise default values from an effective parameter will do. @param skip The number of matching parameters to skip before selecting a parameter. @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. @see #Get(String, boolean) @see #Parameter(String, boolean, int) */ public Vector Get ( String pathname, boolean specific, int skip ) {return Get (Parameter (pathname, specific, skip));} /** Gets the value(s) for a parameter at a specific pathname.

This is the same as using the {@link #Get(String, boolean) Get} method with the specific argument being true.

@param pathname A parameter pathname. @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. */ public Vector Get_Specific ( String pathname ) {return Get (pathname, true);} /** Gets one String value from the specific pathname.

@param pathname A parameter pathname. @return The first String value from the specific parameter, or null if there is no parameter value. @see #Get_Specific(String) @see #Get_One(Vector) */ public String Get_Specific_One (String pathname) {return Get_One (Get (pathname, true));} /** Gets the value(s) for the effective parameter at a pathname.

This is the same as using the {@link #Get(String, boolean) Get} method with the specific argument being false.

@param pathname A parameter pathname. @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. */ public Vector Get ( String pathname ) {return Get (pathname, false);} /** Gets one String value for the effective parameter at a pathname.

@param pathname A parameter pathname. @return The first String value from the effective parameter, or null if there is no value. @see #Get(String) @see #Get_One(Vector) */ public String Get_One (String pathname) {return Get_One (Get (pathname, false));} /** Gets the value(s) for the effective parameter at a pathname where the parameter may be linked to another parameter through its value.

A linked parameter is an assignment of a single String value in which the value is the pathname of some other effective parameter. If the parameter has more than one value it is not linked. The returned Vector contains the value(s) of the first non-linked parameter found. An empty Vector will be returned if no parameter is found.

@param pathname A parameter pathname. @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. */ public Vector Get_Linked ( String pathname ) { if ((DEBUG & DEBUG_GET) != 0) System.out.println (">>> Configuration.Get_Linked: \"" + pathname + "\""); Vector value = Get (pathname), link; while (value.size () == 1 && (link = Get (Get_One (value))).size () != 0) { if ((DEBUG & DEBUG_GET) != 0) System.out.println (" Linked to: " + link); value = link; } if ((DEBUG & DEBUG_GET) != 0) System.out.println ("<<< Configuration.Get_Linked: " + value); return value; } /** Gets one String value for the effective, and possibly linked, parameter at a pathname.

@param pathname A parameter pathname. @return The first String value from the effective parameter, or null if there is no value. @see #Get_Linked(String) @see #Get_One(Vector) */ public String Get_Linked_One (String pathname) {return Get_One (Get_Linked (pathname));} /** Gets the value(s) for a Parameter.

@param parameter The Parameter from which to get value(s). @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. @see #Get(Value) */ public Vector Get ( Parameter parameter ) { Value value = null; if (parameter != null) { if ((DEBUG & DEBUG_GET) != 0) System.out.println ("Configuration.Get: parameter \"" + parameter.Path_Name () + "\""); try {value = parameter.Value ();} catch (PVL_Exception exception) {/* The parameter is not an Assignment. */} } return Get (value); } /** Gets one String value from the Get Vector.

@param parameter The Parameter from which to get a value. @return The first String value from the Vector, or null if there is no value. @see #Get(Parameter) @see #Get_One(Vector) */ public String Get_One ( Parameter parameter ) {return Get_One (Get (parameter));} /** Gets the value(s) for a Parameter Value.

@param value The Value from which to get value(s). @return A Vector of zero or more values. Each element of the Vector is either the String representation of a single value, or is itself a Vector of the same kind. */ public static Vector Get ( Value value ) { if ((DEBUG & DEBUG_GET) != 0) System.out.println ("Configuration.Get value: " + value); Vector values = new Vector (); if (value != null) { if (value.Is_Array ()) { ListIterator list = value.listIterator (); while (list.hasNext ()) { value = (Value)list.next (); if (value.Is_Array ()) values.add (Configuration.Get (value)); else { try {values.add (value.String_Data ());} catch (PVL_Exception exception) {/* Shouldn't happen. */} } } } else { try {values.add (value.String_Data ());} catch (PVL_Exception exception) {/* Shouldn't happen. */} } } return values; } /** Gets one String value from the Get Vector.

@param value The Value from which to get a value. @return The first String value from the Vector, or null if there is no value. @see #Get(Value) @see #Get_One(Vector) */ public String Get_One (Value value) {return Get_One (Get (value));} /** Gets the first String value from a Vector of values.

@param values The Vector to search. @return The first String from the Vector, or null if none is found. @see #Get_One(Vector, int) */ public static String Get_One ( Vector values ) {return Get_One (values, 0);} /** Gets the indexed String value from a Vector of values.

The Vector is searched for the first non-Vector Object and its {@link Object#toString() toString} value is returned. Vector Objects are searched as they are encountered (i.e. depth-wise). Since the Vector is presumed to be the values Vector returned from a Get method, the first non-Vector Object should be the first String value of a Configuration parameter (nevertheless, all Objects should have a toString method).

@param values The Vector to search. @param index The index of the element in the values. @return The indexed String from the Vector, or null if none is found. */ public static String Get_One ( Vector values, int index ) { if (values != null && values.size () > 0 && values.size () > index) { Object value = values.elementAt (index); if (value instanceof Vector) return Get_One ((Vector)value, 0); return value.toString (); } return null; } /** Gets a numerical value.

The first available parameter value found at the pathname is converted to a binary double value.

@param pathname A parameter pathname. @param default_value The default value to use if the parameter can not be found. @return The numerical value of the parameter, or the default value if the parameter can not be found or does not have a numerical value. */ public double Get_Number ( String pathname, double default_value ) { String value = Get_One (pathname); if (value != null) { try {return Double.parseDouble (value);} catch (NumberFormatException exception) {} } return default_value; } /** Tests if a parameter has a logically true value.

The first available parameter value found at the pathname is examined for a logically true value. A paremeter is considered to have a logically true value if it is any one of (case ignored): "ENABLED", "TRUE", "YES", "ON", "Y", or "1".

@param pathname A parameter pathname. @param default_flag The default value to use if the parameter can not be found. @return true if the parameter has an enabled value; false otherwise. The default_flag value is returned if the parameter can not be found. */ public boolean Enabled ( String pathname, boolean default_flag ) { String value = Get_One (pathname); if (value == null) return default_flag; if (value.equalsIgnoreCase ("ENABLED") || value.equalsIgnoreCase ("TRUE") || value.equalsIgnoreCase ("YES") || value.equalsIgnoreCase ("ON") || value.equalsIgnoreCase ("Y") || value.equalsIgnoreCase ("1")) return true; return false; } /** Gets the Configuration parameters for a group.

All parameters for a named group, plus all default parameters for the group, are collected into a new Configuration that will have the name of the group. Default parameters are all Assignment parameters in all parents of the group parameter. These parameters are {@link Configuration#Set_Conditionally(String, Object) Set_Conditionally} in the new configuration such that parameters closest to the group (a parent is closer than a grandparent) have precedence when there are parameters with the same name.

If an Assignment parameter with the group name is found instead of an Aggregate parameter, its value is taken to be the name of the Aggregate parameter name to use. Such a parameter is a "link" to the actual group parameter, which gives the group parameter an "alias" name. This allows configuration parameter groups to be known by different names. Link parameters may refer to other link parameters. If the extracted configuration is an alias it will be given an {@link Configuration#ALIAS ALIAS} parameter having the value that is the name of the alias group.

@param group The name of the configuration group to extract. @return A Configuration with all of the group's parameters, or null if the group could not be found. @throws Configuration_Exception If the Parameter group could not be assembled into a new Configuration. */ public Configuration Group ( String group ) throws Configuration_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Configuration.Group: " + group); Configuration group_configuration = null; Parameter parameter = null; String alias = group; while ((parameter = Find (alias, Case_Sensitive, parameter)) != null && ! parameter.Is_Aggregate ()) { if (parameter.Is_Assignment ()) { try { // Follow name aliases (links). if (parameter.Value () != null && ! (alias = parameter.Value ().String_Data ()).equals ("") && ! alias.equals (group)) { // Find the new name from the top. parameter = null; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" alias: " + alias); } } catch (PVL_Exception exception) {/* Shouldn't happen */ return null;} } } if (parameter != null) { group_configuration = new Configuration (parameter); if (! alias.equals (group)) { group_configuration.Set (Configuration.ALIAS, alias); group_configuration.Name (group); } // Bring down all the Assignments from the parents. Parameter parent = parameter; while ((parent = parent.Parent ()) != null) { ListIterator list = parent.listIterator (); while (list.hasNext ()) { parameter = (Parameter)list.next (); try { if (parameter.Is_Assignment ()) group_configuration.Set_Conditionally (new Parameter (parameter)); } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to copy group parameter \"" + parameter.Name () + "\"." + exception_message (exception), exception ); } } } group_configuration.Coalesce (); } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Configuration.Group: " + (! (group_configuration == null))); return group_configuration; } /*----------------------------------------------------------------------------- */ /** Sets a parameter at a specified pathname location to the value of an Object.

The Object may be of any type acceptable for use in creating a Parameter Value. This includes Vectors of Objects which will set the parameter to the corresponding Value array. A null value will result in an empty, Token, parameter.

When the value of an existing parameter is not to be replaced, the availability of an {@link #Effective_Parameter(String) Effective_Parameter} is sufficient to prevent the creation of a new parameter. However, when replace is true, a parameter will be created at the specific pathname if it does not already exist. Note: If an existing parameter is found for the pathname, whether specific or effective, that does not have a value, its always assigned the new value.

Only parameters assigned a value - not parameter groups - may have their value set. If the parameter does not exist a new parameter is created and assigned the value.

@param pathname The pathname of the parameter whose value is to be set. @param value The value that will be assigned to the parameter. @param replace If true, replace the values of existing parameters with the new value; if false, do not replace existing values. @return true if a previous value was replaced, or false if the parameter is being set for the first time. @throws Configuration_Exception If a new parameter could not be created (possibly because the pathname is null), or the value could not be set. @see Parameter#Value(Object) */ public synchronized boolean Set ( String pathname, Object value, boolean replace ) throws Configuration_Exception { if (pathname == null) throw new Configuration_Exception ( ID + NL + "Unable to Set a parameter with a null pathname." ); if ((DEBUG & DEBUG_SET) != 0) System.out.println (">>> Set: " + pathname + " to " + value + " (replace = " + replace + ")"); // Use effective parameters when not replacing (replace == specific). Parameter parameter = Parameter (pathname, replace); if ((DEBUG & DEBUG_SET) != 0) System.out.println ("Set: " + pathname + ((parameter == null) ? " not found." : " found.")); Value old_value = null; if (parameter == null) { // Create a new (initially empty) parameter. parameter = group_parameter (pathname); if ((DEBUG & DEBUG_SET) != 0) System.out.println ("Set: new parameter - " + parameter.Path_Name ()); } else { // Get the previous value. try {old_value = parameter.Value ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } if (replace || old_value == null) { // Replace the parameter's value. if ((DEBUG & DEBUG_SET) != 0) System.out.println ("Set: replacing " + old_value + " with " + value); try {parameter.Value (value);} catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to Set parameter \"" + pathname + "\"" + " to value \"" + value + "\"." + exception_message (exception), exception ); } } if ((DEBUG & DEBUG_SET) != 0) System.out.println ("<<< Set: Previous value - " + ((old_value == null) ? "false" : "true")); return (old_value == null) ? false : true; } /** Sets the value of a parameter at a pathname.

A new parameter is created with the value if a parameter with the pathname does not exist. Otherwise the existing value is replaced. This is the same as using the {@link #Set(String, Object, boolean) Set} method with a replace argument of true.

@param pathname The pathname of the parameter whose value is to be set. @param value The value that will be assigned to the parameter. @return true if a previous value was replaced, or false if the parameter is being set for the first time. @see #Set(String, Object, boolean) */ public boolean Set ( String pathname, Object value ) throws Configuration_Exception {return Set (pathname, value, true);} /** Sets the value of a new parameter at a pathname.

A new parameter is created with the value if a parameter with the pathname does not exist. Otherwise the existing value is left unchanged. This is the same as using the {@link #Set(String, Object, boolean) Set} method with a replace argument of false.

@param pathname The name of the new parameter. @param value The value that will be assigned to the parameter. @return true if a previous value was replaced, or false if the parameter is being set for the first time. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(String, Object, boolean) */ public boolean Set_Conditionally ( String pathname, Object value ) throws Configuration_Exception {return Set (pathname, value, false);} /** Sets all of the entries of an array of String arrays as Configuration parameters.

Each entry of the primary array is taken to be an array of Strings. The first String is a parameter pathname and all remaining Strings in the array are values for the parameter. A parameter pathname without any following parameter value(s) will be skipped.

@param parameter_table The table of parameter names and values. @param replace If true, replace the values of existing parameters with the new value; if false, do not replace existing values. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(String, Object, boolean) */ public void Set ( String[][] parameter_table, boolean replace ) throws Configuration_Exception { Vector values = new Vector (); for (int element = 0; element < parameter_table.length; element++) { values.clear (); int value; for (value = 1; value < parameter_table[element].length; value++) values.add (parameter_table[element][value]); if (value > 1) { try { if (values.size () == 1) // Single value (don't create an Array Value). Set ( parameter_table[element][0], (String)values.elementAt (0), replace ); else Set ( parameter_table[element][0], values, replace ); } catch (Configuration_Exception exception) { throw new Configuration_Exception ( ID + NL + "During Set of \"" + parameter_table[element][0] + "\" Strings array entry " + element + "." + exception_message (exception), exception ); } } } } /** Sets all of the entries of an array of String arrays.

An existing parameter has its value replaced, otherwise a new parameter is created

@param parameter_table The table of parameter names and values. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(String[][], boolean) @see #Set(String, Object) */ public void Set ( String[][] parameter_table ) throws Configuration_Exception {Set (parameter_table, false);} /** Conditionally sets all of the entries of an array of String arrays.

New parameters are created; existing parameters are unchanged.

@param parameter_table The table of parameter names and values. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(String[][], boolean) @see #Set_Conditionally(String, Object) */ public void Set_Conditionally ( String[][] parameter_table ) throws Configuration_Exception {Set (parameter_table, false);} /** Sets parameters in the Configuration to correspond with the contents of another parameter.

For an Assignment parameter, a parameter in the Configuration at the same pathname location is assigned the same parameter value. For a parameter group, all of its members are recursivley set in the Configuration.

The {@link Parameter#Path_Name() pathname} of each parameter that is set is made to appear to be rooted at the Configuration root. Thus the location of each parameter set in the Configuration will be at the same relative location of the source parameter in its own hierarchy according to its own pathname. Referencing a source parameter nested somewhere down in a hierachy will then produce a Configuration parameter that is nested down to the same relative location. However, when the intention is to root the Configuration parameters relative to the source parameter being at the root itself, then the source parameter should be copied before being provided to this method:

Set (new Parameter (parameter), replace);

Note: Freestanding source parameters (that are not located in a group hierarchy) set Configuration parameters located at the root.

@param parameter The source Parameter for setting in the Configuration. @param replace If true, replace the values of existing parameters with the new value; if false, do not replace existing values. @throws Configuration_Exception If there was a problem setting any parameter in the Configuration. @see #Set(String, Object, boolean) */ public void Set ( Parameter parameter, boolean replace ) throws Configuration_Exception { if (parameter == null) return; if ((DEBUG & DEBUG_SET) != 0) System.out.println (">>> Set:" + NL + " parameter: " + parameter.Path_Name () + NL + " class: " + parameter.Classification_Name () + NL + " replace: " + replace); String pathname; if (parameter.Is_Aggregate ()) { ListIterator list = parameter.listIterator (); while (list.hasNext ()) { Parameter new_parameter = (Parameter)list.next (); if ((DEBUG & DEBUG_SET) != 0) System.out.println (" Set Aggregate entry " + new_parameter.Path_Name ()); pathname = Path_Name_to_Pathname (new_parameter.Path_Name ()); if (new_parameter.Is_Aggregate () && new_parameter.Is_Empty ()) group_parameter (pathname) .Classification (new_parameter.Classification ()); else { try {Set (new_parameter, replace);} catch (Configuration_Exception exception) { throw new Configuration_Exception ( ID + NL + "During Set of parameter group \"" + parameter.Name () + '"' + NL + " at entry " + list.previousIndex () + "." + exception_message (exception), exception ); } } } } else { pathname = Path_Name_to_Pathname (parameter.Path_Name ()); if (pathname.length () == 0) pathname = parameter.Path_Name (); // Use specific pathname. if (replace || Parameter (pathname, true) == null) { Value value = null; try {value = parameter.Value ();} catch (PVL_Exception exception) {} Set (pathname, value); // Just let Set throw any exception. } } if ((DEBUG & DEBUG_SET) != 0) System.out.println ("<<< Set parameter " + parameter.Path_Name ()); } /** Sets parameters in the Configuration to correspond with the contents of another parameter.

An existing Configuration parameter has its value replaced, otherwise a new parameter is created

@param parameter The source Parameter for setting in the Configuration. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(Parameter, boolean) */ public void Set ( Parameter parameter ) throws Configuration_Exception {Set (parameter, true);} /** Conditionally sets parameters in the Configuration to correspond with the contents of another parameter.

New parameters are created; existing parameters are unchanged.

@param parameter The source Parameter for setting in the Configuration. @throws Configuration_Exception If there was a problem setting any parameter value. @see #Set(Parameter, boolean) @see #Set_Conditionally(String, Object) */ public void Set_Conditionally ( Parameter parameter ) throws Configuration_Exception {Set (parameter, false);} /** Sets all occurances of a parameter with the specified name to have the specified value.

Only existing parameters have their value reset. If there are no parameters with the specified name, no new parameter is created.

@param name The parameter name to find. This should be a simple name, not a pathname. If a pathname is specified, then only parameters at the pathname will be modified; an absolute pathname can only apply to a single parameter, but a relative pathname may apply to more than one. @param value The Object to be assigned as the value of each parameter found. @return true if at least one parameter was found, false otherwise. @throws Configuration_Exception If there was a problem replacing a parameter's value. */ public synchronized boolean Set_All ( String name, Object value ) throws Configuration_Exception { if ((DEBUG & DEBUG_SET) != 0) System.out.println (">>> Set_All (\"" + name + "\", " + value + ")"); boolean found = false; Parameter pattern = new Parameter (name); Selector selection = new Selection () .Name (true) .Specific (Case_Sensitive); Parameter parameter = null; while ((parameter = Find (pattern, selection, parameter)) != null) { if (parameter.Is_Aggregate ()) continue; found = true; try { // Replace the parameter's value. if ((DEBUG & DEBUG_SET) != 0) System.out.println ("Set_All: replacing " + parameter.Path_Name () + ":" + parameter.Value ()); parameter.Value (value); } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to Set parameter \"" + parameter.Path_Name () + "\"" + " to value \"" + value + "\"." + exception_message (exception), exception ); } } return found; } /** Returns the group Parameter at the pathname.

This Parameter is created (as an empty Parameter) if it doesn't exist; which means that any and all of its parent Parameters must exist or will be created, too. If the pathname is relative (i.e. does not begin with a {@link Parameter#Path_Delimiter() path delimiter}) and the first segment of the pathname does not exist, then it will be created at the root of the Configuration. Note The Parameter returned will not be a group parameter if, and only if, a new parameter was created.

@param pathname The pathname to a group parameter. @return The Parameter at the pathname location. @throws Configuration_Exception If there is a problem creating any segment of the pathname, including if the pathname is null. */ private synchronized Parameter group_parameter ( String pathname ) throws Configuration_Exception { if ((DEBUG & DEBUG_SET) != 0) System.out.println ("... group_parameter: " + pathname); if (pathname == null) throw new Configuration_Exception ( ID + NL + "Null pathname for group_parameter!" ); // Find only group parameters with the specified name. Selector selection = new Selection () .Name (true) .Specific (Case_Sensitive) .And (true) .Classification (true); Parameter pattern; try {pattern = new Parameter (pathname).Add (new Parameter ());} catch (PVL_Exception exception) { // This shouldn't happen. throw new Configuration_Exception ( ID + NL + "Unable to construct group parameter pattern parameter!" + exception_message (exception), exception ); } Parameter parameter = Find (pattern, selection); if ((DEBUG & DEBUG_SET) != 0 && parameter != null) System.out.println (" group_parameter: Found " + pathname); if (parameter == null) { // No such parameter. Create a new one. parameter = new Parameter (Basename (pathname)); try { // Get the parent Parameter in which to put it. if ((pathname = Parent_Pathname (pathname)) != null) { if ((DEBUG & DEBUG_SET) != 0) System.out.println (" group_parameter: Add " + parameter.Path_Name () + " to " + pathname); group_parameter (pathname).Add (parameter); } else { // There is no parent (the pathname is at the root). if ((DEBUG & DEBUG_SET) != 0) System.out.println (" group_parameter: Add " + parameter.Path_Name () + " at root."); Add (parameter); } } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to add a new \"" + pathname + "\" parameter." + exception_message (exception), exception ); } } return parameter; } /*----------------------------------------------------------------------------- */ /** Removes the parameter(s) at a pathname.

@param pathname A parameter pathname. @param specific If true, remove only the parameter at the specific pathname, otherwise remove all effective parameters for the pathname. @return true if a previous value was removed, or false if there is no parameter at the pathname. */ public synchronized boolean Remove ( String pathname, boolean specific ) { Parameter parameter; boolean removed = false; if (specific) { if ((parameter = Specific_Parameter (pathname)) != null) { parameter.Parent ().Remove (parameter); return true; } } else { while ((parameter = Effective_Parameter (pathname)) != null) { parameter.Parent ().Remove (parameter); removed = true; } } return removed; } /** Removes the all of the effective parameters for the pathname.

This is the same as using the {@link #Remove(String, boolean) Remove} method with the specific argument being false.

@param pathname A parameter pathname. @return true If a previous value was removed, or false if there were no effective parameters for the pathname. */ public boolean Remove ( String pathname ) {return Remove (pathname, false);} /** Removes the parameter at the specific pathname.

This is the same as using the {@link #Remove(String, boolean) Remove} method with the specific argument being true.

@param pathname A parameter pathname. @return true If a previous value was removed, or false if there was no parameter at the pathname. */ public boolean Remove_Specific ( String pathname ) {return Remove (pathname, true);} /** Removes a group of parameters at a pathname.

The group of parameters at the pathname is removed.

@param pathname A group pathname. @return true If a group was removed, or false if there was no group at the pathname. */ public boolean Remove_Group ( String pathname ) { boolean removed = false; Parameter parameter; if ((parameter = Parameter_at (pathname)) != null && parameter.Is_Aggregate ()) { parameter.Parent ().Remove (parameter); removed = true; } return removed; } /*----------------------------------------------------------------------------- */ /** Gets the name of the source from which the Configuration parameters have been set.

@return The source name String for the Configuration. This will be a source file pathname or URL if the Configuration was constructed from such a source. This will be {@link #DEFAULTS DEFAULTS} if the Configuration is based soley on the internal default parameters. It will be the {@link Parser#CONTAINER_NAME} if the Configuration was constructed from an Assignment Parameter (including an empty Parameter) rather than an Aggregate. If the Configuration was constructed from an Aggregate Parameter it will be the name of the Aggregate Parameter. */ public String Source () {return Config_Source;} /** Gets the name of the file from which the Configuration parameters have been set.

@deprecated Replaced by {@link #Source()} */ public String Filename () {return Source ();} /** Resets the Configuration from the contents of the source.

@param source The name of the configuration source. @return This Configuration. @throws Configuration_Exception If the source can not be read or successfully parsed for parameters, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see #Configure(String) */ public Configuration Source ( String source ) throws Configuration_Exception { Configure (source); return this; } /** Resets the Configuration from the contents of the source file.

@param filename The name of the configuration file. @deprecated Replaced by {@link #Source(String)} */ public Configuration Filename ( String filename ) throws Configuration_Exception {return Source (filename);} /** Sets the default source name.

@param source The name of the new default source. If null, the {@link #DEFAULTS DEFAULTS} name will be used. */ public static void Default_Source ( String source ) { if ((Default_Source = source) == null) Default_Source = DEFAULTS; } /** Sets the default filename.

@param filename The new default filename. If null, the {@link #DEFAULTS DEFAULTS} name will be used. @deprecated Replaced by {@link #Default_Source(String)} */ public static void Default_Filename ( String filename ) {Default_Source (filename);} /** Gets the default source name.

@return The default source name. @see #Default_Source(String) */ public static String Default_Source () {return Default_Source;} /** Gets the default source name.

@return The default source name. @deprecated Replaced by {@link #Default_Source()} */ public static String Default_Filename () {return Default_Source ();} /** Sets the class relative to which a configuration file may be sought.

@param related_class The Class related to the configuration file to be loaded. @see #Configure(String) */ public static void Relative_to_Class ( Class related_class ) {Relative_to_Class = related_class;} /** Gets the class relative to which a configuration file may be sought.

@return The Class related to the configuration file to be loaded. @see #Relative_to_Class(Class) */ public static Class Relative_to_Class () {return Relative_to_Class;} /** Ensure that this Configuration is an Aggregate.

If this Configuration is an Aggregate, nothing is done.

If this Configuration is not an Aggregate, it is made into one. If this Configuration has the UNKNOWN classification (it is empty) it's Classification is simply changed to GROUP. Otherwise a copy of the Parameter that is currently this Configuration is added to this Configuration, thus making this Configuration an Aggregate while retaining the current Parameter. Then the name of this Configuration is set to {@link Parser#CONTAINER_NAME}. */ private void Ensure_Aggregate_Configuration () { if (! Is_Aggregate ()) { if (Is_Unknown ()) Classification (GROUP); else { try {Add (new Parameter (this));} catch (PVL_Exception exception) {/* This is a good Parameter */} } Name (Parser.CONTAINER_NAME); } } /*----------------------------------------------------------------------------- */ /** Sets the case sensitive mode for finding parameter names. It is initially disabled by default.

@param enable If true, case sensitive matching is used. @return The previous case sensitive mode; */ public boolean Case_Sensitive ( boolean enable ) { boolean previous_state = Case_Sensitive; Case_Sensitive = enable; return previous_state; } /** Gets the case sensitive mode for finding parameter names.

@return The case sensitive mode: true if case sensitive matching is enabled. @see #Case_Sensitive(boolean) */ public boolean Case_Sensitive () {return Case_Sensitive;} /** Enables or disables automatic inclusion of {@link #Defaults} parameters.

N.B.: This method controls the automatic inclusion of {@link #Defaults} parameters for subsequent Configure operations. The {@link #Defaults_Default default automatic defaults} state is applied to the construction of new Configuration objects.

@param enable true if Defaults parameters are to be automatically included; false otherwise. @return The previous automatic Defaults state. */ public boolean Defaults ( boolean enable ) { boolean state = Include_Defaults; Include_Defaults = enable; return state; } /*============================================================================== Methods */ /** Provides a String description of a Configuration formatted as a list of the parameters and their values.

The description is suitable for saving to a file for later input.

@return The String listing all of the Configuration parameters. */ public String Description () { StringWriter description = new StringWriter (); String name = Name (); description.write (Parser.CROSSHATCH); description.write (" " + name + NL); if (Config_Source != null) description.write (Parser.CROSSHATCH + " From: " + Config_Source + NL); Lister lister = new Lister (description).Strict (false); // Prevent the top level from being written. Name (Parser.CONTAINER_NAME); try {lister.Write (this);} catch (Exception exception) { Name (name); // Restore the original name. return exception.getMessage (); } Name (name); // Restore the original name. if (lister.Warning () != null) { String_Buffer warning = new String_Buffer (lister.Warning ().getMessage ()); warning.escape_to_special (); description.getBuffer () .append (NL + "Warning:" + NL) .append (warning.toString ()); } return description.toString (); } /*----------------------------------------------------------------------------- */ /** Loads a Configuration from a configuration file.

If the source String is null or empty, the {@link #Default_Source() Default_Source} String is used. If this is null the {@link #DEFAULTS} name is used.

If the source String equals the {@link #DEFAULTS} name the Configuration is {@link #Configure(InputStream) loaded from a null stream}.

If a URL can be constructred from the source String the Configuration is {@link #Configure(URL) loaded from the URL}.

An attempt is made to open a file input stream use the source String as the file's pathname. If this fails and the source String is a simple filename - it has no directory components - the user's home directory pathname is prepended and another attempt is made to open a file input stream (unless the simple filename is for the same file in the user's home directory). If a stream is opened it is used to {@link #Configure(InputStream) load} the Configuration.

The source String is then treated as a {@link ClassLoader#getSystemResource(String) system resource location} in the JVM classpath. This includes finding configuration files located in jar files. If this fails, a {@link #Relative_to_Class(Class) relative-to Class} has been specified and the source String is a simple filename then the class's package pathname, if it can be determined, is prepended to the source name and another attempt is made to locate the system resource. If a system resource location is found the Configuration is {@link #Configure(URL) loaded from the URL}.

When a input source is found the {@link #Source() Source} is set to the name of the source. If the source is a local file the absolute pathname will be used. If the source is a URL, including a system resource location URL, the URL will be used.

@param source A URL specification, pathname of a disk file or jar resource, or the special {@link #DEFAULTS DEFAULTS} name. If null, the {@link #Default_Source() Default_Source} will be used. @throws Configuration_Exception If the source does not refer to a source that can be found, read or successfully parsed for parameters, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @throws IllegalArgumentException If no configuration source can be found for the source name. @see #Configure(URL) @see #Configure(InputStream) @see #Source() */ public synchronized void Configure ( String source ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> Configure (source: " + source + ")"); if (source == null || source.length () == 0) source = Default_Source; if (source == null || source.length () == 0) source = DEFAULTS; String config_source = null; FileInputStream stream = null; if (! source.equals (DEFAULTS)) { // Try to construct a URL from the source String. URL url = null; try { url = new URL (source); Configure (url); return; } catch (MalformedURLException exception) {/* Nope. Fall back... */} // Try to find a local file. File file = new File (source); config_source = file.getAbsolutePath (); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure: Trying " + config_source); try {stream = new FileInputStream (file);} catch (FileNotFoundException exception) { /* Nope. Fall back... */ if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure: No configuration file " + config_source); } if (stream == null && file.getName ().equals (source)) { /* Just a simple filename. Try looking for it in the user's home directory. */ String pathname = System.getProperty ("user.home") + File.separator + source; if (! pathname.equals (config_source)) { config_source = pathname; if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure: Trying " + pathname); try {stream = new FileInputStream (pathname);} catch (FileNotFoundException exception) { /* Nope. Fall back... */ if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure: No configuration file " + pathname); } } } if (stream == null) { /* Try for a URL from a System Resource at the source location. This can find a file inside a jar file. */ url = ClassLoader.getSystemResource (source); if (url == null && Relative_to_Class != null && file.getName ().equals (source)) { // Try for a class relative location. try {url = ClassLoader.getSystemResource (Relative_to_Class.getPackage ().getName () .replace ('.', File.separatorChar) + File.separator + source);} catch (NullPointerException except) {} } if (url == null) { /* If url is null, then we were unable even to use the source string to get a System Resource. At this point, we are out of reasonable options and must throw an exception. */ throw new IllegalArgumentException ( ID + NL + "Configure can't find source file \"" + source + "\"." ); } Configure (url); return; } } if (config_source != null) Config_Source = config_source; Configure (stream); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Configure (source: " + source + ")"); } /** Loads a Configuration from a URL.

The URL is used to open an InputStream as a source of configuration parameters. If the URL is null then a null stream will be used. If a stream is successfully opened from the URL, then the Configuration's {@link #Source() Source} will be set to the URL String. The Configuration is {@link #Configure(InputStream) loaded from the InputStream}.

@param url The URL source of configuration parameters. May be null. @throws Configuration_Exception If an InputStream can not be opened from the URL, the stream could not be successfully parsed for parameters, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see #Configure(InputStream) */ public void Configure ( URL url ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> Configure (URL: " + url + ")"); InputStream stream = null; if (url != null) { try {stream = url.openStream ();} catch (IOException exception) { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Unable to open a stream from the URL " + url + NL + exception.getMessage ()); throw new Configuration_Exception ( ID + NL + "Configure can't open URL -" + NL + url + exception_message (exception), exception ); } Config_Source = url.toString (); } Configure (stream); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Configure (URL: " + url + ")"); } /** Loads a Configuration from an InputStream.

The stream is parsed for configuration parameters and their values (parameter = value).

This method will first {@link Parameter#Remove_All() Remove_All} current parameters from the Configuration. The InputStream is used to construct a Parser from which all available parameters are {@link Parameter#Add(Parser) Add}ed into this Configuration. If automatic {@link #Defaults(boolean) Defaults} are enabled the internal parameter {@link #Defaults Defaults} are then {@link #Set_Conditionally(Parameter) Set_Conditionally}. If automatic file {@link #Include(boolean) Include}s is enabled any {@link #INCLUDE} parameters are found and the parameters from the files they reference are {@link #Include() Include}d. Finally all parameters are {@link #Coalesce() Coalesce}d together.

If, however, the InputStream is null, then only the Defaults will be used. The Configuration's {@link #Source() Source} will be set to the special {@link #DEFAULTS DEFAULTS} name.

If this Configuration's Source is not null, then the {@link Parameter#Name() Name} will be set to the same String; otherwise it will simply be set to "InputStream."

@param stream The InputStream source of configuration parameters. @throws Configuration_Exception If the stream can not be read or successfully parsed for parameters, or there was a problem coalescing the parameters (probably due to a duplicate parameter name). @see Parser#Parser(InputStream) */ public synchronized void Configure ( InputStream stream ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> Configure (InputStream)"); // Clear all current parameters. Remove_All (); if (stream != null) { try { // Add all parameters parsed from the stream. Add ((Parser)(new Parser (stream) .Strict (false) .Crosshatch_Comments (true) .Filter_Input (false))); } catch (PVL_Exception exception) { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure can't Add Parameters from InputStream Parser -" + NL + exception.Message ()); if (exception.Message ().equals (PVL_Exception.FILE_IO)) throw new Configuration_Exception ( ID + NL + "Configure can't read Parameters from InputStream Parser." + exception_message (exception), exception ); else throw new Configuration_Exception ( ID + NL + "Configure couldn't get Parameters from InputStream Parser." + exception_message (exception), exception ); } } else { // DEFAULTS if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("Configure: Using " + DEFAULTS); Config_Source = DEFAULTS; } if (Config_Source != null) { /* Set the Configuration name to the name of its source file. Only the basename of Config_Source is used because the full pathname is likely to contain delimiters that are the same as the Parameter reference path delimiter character. This will confuse operations that use parameter pathname references which are assembled from parameter names. */ File filename = new File (Config_Source); Name (filename.getName ()); } else Name ("InputStream"); // Ensure that an empty or single parameter configuration is a group. Ensure_Aggregate_Configuration (); if (Include_Defaults) // Make sure all of the default parameters have been set. Set_Conditionally (Defaults); if (Include_Includes) // Include all @include files. Include (); // Coalesce all the parameters. Coalesce (); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Configure (InputStream)"); } /** Adds environment variables to the Configuration.

All environment variables that can be obtained from the runtime environment are added to the {@link #ENVIRONMENT} group. If the group doesn't exist, it is appended to the end of the Configuration. Each environment variable is used to conditionally set a parameter in the Environment group; i.e. any existing parameters with the same name as an environment variable do not have their values changed. The value of an environment variable is always a single String regardless of embedded spaces or commas.

@return This Configuration. @see Host#Environment() */ public Configuration Add_Environment () throws Configuration_Exception { Parameter environment_group = null; try {environment_group = group_parameter (ENVIRONMENT);} catch (Configuration_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to add Environment group." + exception_message (exception), exception ); } if (! environment_group.Is_Aggregate ()) environment_group.Classification (Parameter.GROUP); Properties environment_variables = Host.Environment (); Enumeration variables = environment_variables.propertyNames (); while (variables.hasMoreElements ()) { String name = (String)variables.nextElement (); try { Set_Conditionally (ENVIRONMENT + Path_Delimiter () + name, environment_variables.getProperty (name)); } catch (Configuration_Exception exception) { throw new Configuration_Exception ( ID + NL + "Unable to add Environment variable:" + NL + name + " = " + environment_variables.getProperty (name) + exception_message (exception), exception ); } } return this; } /** Enables or disables automatic {@link #Include() Include}s.

N.B.: This method controls the automatic include state for subsequent Configure operations. The {@link #Include_Default Include_Default} state is applied to the construction of new Configuration objects.

@param enable true if automatic file includes is enabled; false otherwise. @return The previous automatic include state. */ public boolean Include ( boolean enable ) { boolean state = Include_Includes; Include_Includes = enable; return state; } /** Enables or disables {@link #Include() Include} file tracing.

Include file tracing inserts a TOKEN parameter at the beginning of the included parameters with a name of the form:

INCLUDE_START:source

where source is the URL or canonical pathname of the file that was included. At the end of the included parameters another TOKEN is inserted that has the same form, but with "START" replaced by "END". If the source can not be found and the {@link #CONTINUE_INCLUDE_ON_ERROR_FLAG} is specified as the include file prefix, then a parameter with a name of the same form, but with "START" replaced by "ERROR", is inserted, and its value is the error message.

Warning: When the resulting parameters are {@link #Coalesce() Coalesce}d the included parameters may be moved to new locations outside the INCLUDE_START to INCLUDE_END sequence. Thus include file tracing can only be advisory.

N.B.: This method controls include file tracing for subsequent Configure operations. The {@link #Include_Tracing_Default Include_Tracing_Default} state is applied to the construction of new Configuration objects.

@param enable true if include file tracing is enabled; false otherwise. @return The previous include file tracing state. */ public boolean Include_Tracing ( boolean enable ) { boolean state = Include_Tracing; Include_Tracing = enable; return state; } /** Includes parameters from include files.

Every parameter named {@link #INCLUDE} is replaced with the parameters included from the file named by the parameter's value. The value may contain nested parameter references that are {@link Reference_Resolver#Resolve(String) Resolve}d against the parameters currently in the Configuration; this includes all parameters that have been included so far.

Relative pathnames for include files are taken to be relative to the directory of the file that contains the include parameter, if this can be determined. This will be the location of the Configuration {@link #Source() file} itself for top level include parameters.

Include parameters may refer to a URL. If this is a "file" URL, then its directory can be used for relative includes contained within it. Other types of URLs may also be used, but they may not contain include parameters with relative filename references. Relative filename references that do not have a including file from which a directory can be determined (URLs or a Configuration that was not constructed from a file with an identified pathname) will be located using the same process as that used to {@link #Configure(String) Configure} from a source name.

Recursive includes - an include parameter that refers to a parent include file - are not allowed (throw a Configuration_Exception).

Parameters that are Aggregates or the Value is an Array are ignored.

An include reference that begins with the {@link #CONTINUE_INCLUDE_ON_ERROR_FLAG} will not cause an exception to be thrown if the reference can not be accessed. This is particularly useful if the existence of the include file is uncertain and its lack is acceptable.

@return This Configuration. @throws Configuration_Exception If an include filename contains a nested reference that can not be resolved; would result in a recursive include; does not refer to a source that can be found, read or successfully parsed for parameters; or there was a problem coalescing its parameters (probably due to a duplicate parameter name). */ public synchronized Configuration Include () throws Configuration_Exception { if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (">>> Configuration.Include"); String config_filename = null, filename, resolved_filename = null, included_pathname, name; /* Stack of included files and their parameters. The pathnames will be canonical pathnames or URLs. */ Stack included_pathnames = new Stack (), included_parameters = new Stack (); // Top level include file is the Configuration file itself (if known). if ((filename = Source ()) != null && ! filename.equals (DEFAULTS)) { if ((config_filename = the_filename (filename)) == null) config_filename = filename; // A URL. if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Top level (Configuration) include: " + config_filename); included_pathnames.push (config_filename); } Reference_Resolver resolver = new Reference_Resolver ().Parameters (this); Configuration include_parameters = null; int include_index; Parameter parent = null, parameter = null; while ((parameter = Find (INCLUDE, false, parameter)) != null) { // The Aggregate containing the include parameter. parent = parameter.Parent (); // The index of the include parameter in it's parent's list. include_index = parent.getIndex (parameter); if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Include found: " + parameter.Path_Name ()); if (! included_parameters.empty () && parameter == included_parameters.peek ()) { /* The include parameter is the last one found. This completes the search of the included parameters. */ if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Previous include."); // Remove the include parameter. parent.Remove (include_index); // Pop the included pathnames and parameters lists. included_pathnames.pop (); included_parameters.pop (); if (config_filename != null && included_pathnames.empty ()) // Retain the Configuration pathname. included_pathnames.add (config_filename); /* Restart the search with the first parameter of the last included parameter's Aggregate list. */ parameter = (Parameter)parent.getChildAt (0); if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Restarting search after " + parameter.Path_Name ()); continue; } try { if (parameter.Is_Aggregate () || parameter.Value ().Is_Array ()) // Ignore Aggregates and Arrays. continue; } catch (PVL_Exception exception) {/* Shouldn't happen */} // Resolve any references in the include parameter value. try {resolved_filename = resolver.Resolve (filename = parameter.Value ().String_Data ());} catch (Throwable exception) { String message = ID + NL + "Include of \"" + filename + '"' + included_list (included_pathnames) + NL + "contains a reference that could not be resolved." + NL + exception.getMessage (); if (exception instanceof ParseException) message += NL + "At location " + ((ParseException)exception).getErrorOffset (); throw new Configuration_Exception (message, exception); } if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Included: " + filename + NL +" resolved: " + resolved_filename); // Check for a continue-on-error flag. boolean continue_on_error = false; if (resolved_filename.startsWith (CONTINUE_INCLUDE_ON_ERROR_FLAG)) { continue_on_error = true; resolved_filename = resolved_filename.substring (CONTINUE_INCLUDE_ON_ERROR_FLAG.length ()); if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" continue: " + resolved_filename); } /* Determine the include pathname. This will produce a canonical pathname for an absolute pathname to an existing file. Otherwise the name remains unchanged. When the name produces a non-file URL, null is returned to flag that no file checking can be done. */ included_pathname = the_filename (resolved_filename); if (included_pathname == null) { // URL. if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" URL"); included_pathname = resolved_filename; } else { if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" File"); File file = new File (included_pathname); if (! included_pathnames.empty () && ! file.isAbsolute ()) { name = the_filename ((String)included_pathnames.peek ()); if (name != null) // Make the included filename relative to the includer file. included_pathname = new File (name).getParent () + File.separator + included_pathname; } if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Source: " + included_pathname); } if (Include_Tracing) { try { // Insert INCLUDE_START before the include parameter. parent.Insert (new Parameter (INCLUDE_START + ':' + included_pathname), include_index++); // Insert INCLUDE_END after the include parameter. parent.Insert (new Parameter (INCLUDE_END + ':' + included_pathname), include_index + 1); } catch (PVL_Exception exception) {/* Shouldn't happen */} } // Load the parameters (turn off auto-include, of course, and Defaults). boolean auto_include = Include (false), defaults = Defaults_Default; Defaults_Default = false; try {include_parameters = new Configuration (included_pathname);} catch (Exception exception) { if (continue_on_error && exception instanceof IllegalArgumentException) { // Remove the INCLUDE parameter. if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" errored -" + NL + exception); parent.Remove (include_index); if (Include_Tracing) { // Insert an INCLUDE_ERROR parameter. try {parent.Insert (new Parameter (INCLUDE_ERROR + ':' + included_pathname) .Value (exception.getMessage ()), include_index);} catch (PVL_Exception except) {/* Shouldn't happen */} } // Restart the search with the parent's first parameter. parameter = (Parameter)parent.getChildAt (0); continue; } throw new Configuration_Exception ( ID + NL + "Include of \"" + included_pathname + '"' + (resolved_filename.equals (filename) ? "" : (NL + "resolved from \"" + filename + '"')) + included_list (included_pathnames) + NL + "is unable to obtain the parameters." + exception_message (exception), exception ); } finally { Include (auto_include); Defaults_Default = defaults; } // Check for recursive file include. if ((included_pathname = the_filename (include_parameters.Source ())) == null) included_pathname = include_parameters.Source (); // URL. if (included_pathnames.contains (included_pathname)) throw new Configuration_Exception ( ID + NL + "Include of \"" + included_pathname + '"' + (resolved_filename.equals (filename) ? "" : (NL + "resolved from \"" + filename + '"')) + included_list (included_pathnames) + NL + "would be recursive." ); if (include_parameters.getChildCount () == 0) { // Empty include parameters list. if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Include list is empty."); parent.Remove (include_index); parameter = null; continue; } // Insert the new parameters before the include parameter. try {parent.Insert (include_parameters.List (), include_index);} catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "Include of \"" + included_pathname + '"' + (resolved_filename.equals (filename) ? "" : (NL + "resolved from \"" + filename + '"')) + included_list (included_pathnames) + NL + "was unable to insert the new parameters!" + exception_message (exception), exception ); } /* Note that the include parameter is left in place to mark the end of the included parameters during the search for more include parameters in the included parameters. */ if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println (" Included parameters:" + NL + include_parameters.Description ()); // Add the included pathname to the list. included_pathnames.push (included_pathname); included_parameters.push (parameter); // Restart the search with the first Parameter in the current Aggregate. parameter = (Parameter)parent.getChildAt (0); } if ((DEBUG & DEBUG_INCLUDE) != 0) System.out.println ("<<< Configuration.Include"); return this; } /** Generates a listing of included files.

The listing is LIFO: the last file included is listed first followed, on succeeding lines, by the file that included the previous file. Each filename is enclosed in quotes and preceded by a newline and "included from ". If the list of included files is empty, the empty String is returned.

N.B.: The listing does not end with a new-line character.

@param included_pathnames The Vector of included filenames. @return The included filenames listing String. */ private static String included_list ( Stack included_pathnames ) { if (included_pathnames.isEmpty ()) return ""; String included = ""; ListIterator names = included_pathnames.listIterator (included_pathnames.size ()); while (names.hasPrevious ()) included += NL + "included from \"" + (String)(names.previous ()) + '"'; return included; } /** Gets a filename from what may be a URL.

An attempt is made to construct a URL from the argument. If this succeeds the protocol is checked to see if it is a "file". If so the canononical path from the URL is returned, otherwise null is returned. If the URL can not be constructed or the canonical path to an existing file can not be determined from a file URL the original argument is returned.

@param string The initial filename. @return A String, or null if the string is a non-file URL. */ private static String the_filename ( String string ) { try { URL url = new URL (string); if (url.getProtocol ().equals ("file")) { File file = new File (string = url.getPath ()); if (file.exists ()) string = file.getCanonicalPath (); // else it's not an existing file. } else // It's a non-file URL. string = null; } catch (MalformedURLException exception) { // Not a URL. File file = new File (string); if (file.isAbsolute () && file.exists ()) { try {string = file.getCanonicalPath ();} catch (IOException except) {} } } catch (IOException exception) {/* Can't get the cononical path */} return string; } /*============================================================================== Utility functions */ /** Gets the pathname for a parameter at the previous group level.

The {@link #Basename(String) Basename} is removed from the pathname. Then the pathname is shortened by one additional segment and the basename appended back again. This has the effect of producing a pathname with the same basename but at the same level as the parent (thus it is the "uncle" to the original pathname :-). This is used when percolating up a Parameter hierarchy looking for an effective (default) parameter of a given name.

@param pathname The pathname to modify. @return The pathname with the parent segment removed, or null if the pathname has no parent segment. */ public static String Pathname_Uncle ( String pathname ) { if (pathname == null) return null; int end = pathname.lastIndexOf (Path_Delimiter ()); if (end <= 0) return null; int start = pathname.lastIndexOf (Path_Delimiter (), end - 1); if (start < 0) return pathname.substring (++end); if (start == 0) return pathname.substring (end); return pathname.substring (0, start) + pathname.substring (end); } /** Gets the Configuration pathname for a Parameter Path_Name.

A Parameter {@link Parameter#Path_Name() Path_Name} includes the name of the root Aggregate parameter. A Configuration pathname does not include the root name segment.

@param pathname A Parameter Path_Name. @return The pathname stripped of the root segment, or the empty String if the pathname only has a root segment. */ public static String Path_Name_to_Pathname ( String pathname ) { if (pathname == null) return null; int end = pathname.indexOf (Path_Delimiter (), 1); if (end > 0) return pathname.substring (end); return ""; } /** Gets the Configuration pathaname for a parameter.

@param parameter A parameter. @return The pathname String for the parameter. @see #Path_Name_to_Pathname(String) */ public static String Pathname ( Parameter parameter ) {return Path_Name_to_Pathname (parameter.Path_Name ());} /** Gets the effective pathname for a specified pathname.

The effective pathname is the pathname of the {@link #Effective_Parameter(String) Effective_Parameter} for the pathname.

@param pathname A parameter pathname. @return The effective pathname for the pathname, or null if there is none. */ public String Effective_Pathname ( String pathname ) { Parameter parameter = Effective_Parameter (pathname); if (parameter != null) return parameter.Path_from (this); return null; } /** Gets the pathname to a parameter group.

Links will be followed: When an Assignment Parameter with the name of the group is found its value will be taken as the new group name to be sought until an Aggregate Parameter is found. A link chain is not allowed to loop back to the original group name.

@param group The name of an Aggregate Parameter to be found. @return The fully qualified pathname to the parameter group, or null if the group can not be found. */ public String Group_Pathname ( String group ) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Configuration.Group_Pathname: " + group); if (group == null) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Configuration.Group_Pathname: " + group); return null; } Parameter parameter = null, pattern = new Parameter (group); Selector selection = new Selection () .Name (true) .Specific (Case_Sensitive); String link = group; while ((parameter = Find (pattern, selection, parameter)) != null && ! parameter.Is_Aggregate ()) { if (parameter.Is_Assignment ()) { try { // Follow links. if (parameter.Value () != null && ! (link = parameter.Value ().String_Data ()).equals ("") && ! (Case_Sensitive ? link.equals (group) : link.equalsIgnoreCase (group))) { // Find the new name from the top. pattern.Name (link); parameter = null; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" link: " + link); } } catch (PVL_Exception exception) {/* Shouldn't happen */ return null;} } } if (parameter != null) link = Path_Name_to_Pathname (parameter.Path_Name ()); else link = null; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Configuration.Group_Pathname: " + link); return link; } /** Gets the parameter for a pathname.

When specific is false and there is no parameter in the Configuration at the specified pathname location, then a parameter with the same {@link #Basename(String) Basename} is sought at progressively higher levels of the path. The first parameter found is returned, or null if no parameter is found.

Note: Only an Assignment (or Token) parameter will be found. Aggregate parameters do not qualify because they have no value. A Token has no value but can be assigned one.

@param pathname A parameter pathname. @param specific true if only a parameter at the specific pathname is acceptable, otherwise an effective parameter will do. @return The Parameter for the pathname, or null if there is none. @see #Pathname_Uncle(String) */ public Parameter Parameter ( String pathname, boolean specific ) {return Parameter (pathname, specific, 0);} /** Gets the Nth parameter for a pathname.

The parameter is selected after skip parameters have been found that match the pathname. If skip is 0, the first matching parameter is selected; if skip is N, N matching parameters must be found before the next matching parameter will be selected.

@param pathname A parameter pathname. @param specific true if only a parameter at the specific pathname is acceptable, otherwise an effective parameter will do. @param skip The number of matching parameters to skip. @return The Parameter for the pathname, or null if there is none found. @see #Parameter(String, boolean) */ public Parameter Parameter ( String pathname, boolean specific, int skip ) { if (pathname == null) return null; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Configuration.Parameter (\"" + pathname + "\", " + specific + ")"); // Find only non-Aggregate parameters with the specified name. Parameter pattern = new Parameter (pathname); Selector selection = new Selection () .Name (true) .Specific (Case_Sensitive); Parameter parameter = null; do { while ((parameter = Find (pattern, selection, parameter)) == null && ! specific) { if ((pathname = Pathname_Uncle (pathname)) == null) break; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" Trying Pathname_Uncle: " + pathname); pattern.Name (pathname); } } // Accept only the Nth non-Aggregate parameter. while (parameter != null && (parameter.Is_Aggregate () || skip-- > 0)); if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Configuration.Parameter: " + (parameter == null ? "not found." : "found")); return parameter; } /** Gets the parameter or parameter group at a pathname.

Only the parameter at the specific pathname will be found. This is similar to the {@link #Parameter(String, boolean) Parameter} method used with specific=true. But unlike that method, this method may return an Aggregate if that is what is found.

@param pathname A parameter pathname. @return The Parameter for the pathname, or null if there is none. */ public Parameter Parameter_at ( String pathname ) { if (pathname == null) return null; Parameter pattern = new Parameter (pathname); Selector selection = new Selection () .Name (true) .Specific (Case_Sensitive); return Find (pattern, selection); } /** Gets the specific Parameter for a pathname.

This is the same as using the {@link #Parameter(String, boolean) Parameter} method with the specfic argument being true.

@param pathname A parameter pathname. @return The Parameter for the pathname, or null if there is none. */ public Parameter Specific_Parameter ( String pathname ) {return Parameter (pathname, true);} /** Gets the effective Parameter for a pathname.

This is the same as using the {@link #Parameter(String, boolean) Parameter} method with the specific argument being false.

@param pathname A parameter pathname. @return The effective Parameter for the pathname, or null if there is none. */ public Parameter Effective_Parameter ( String pathname ) {return Parameter (pathname, false);} /** Sets the duplicate parameter action during coalescing.

One of these actions may be specified:

PREFER_FIRST_PARAMETER (>0)
When a duplicate Assignment Parameter pathname is found while coalescing the Configuration, give preference to the first parameter; i.e. remove all but the first of any duplicate pathname parameters.
PREFER_LAST_PARAMETER (<0)
When a duplicate Assignment Parameter pathname is found while coalescing the Configuration, give preference to the last parameter; i.e. remove all but the last of any duplicate pathname parameters.
THROW_ON_DUPLICATE_PARAMETERS (0)
When a duplicate Assignment Parameter pathname is found while coalescing the Configuration, a Configuration_Exception will be thrown.

The default action is to THROW_ON_DUPLICATE_PARAMETERS.

@param action The duplicate parameter action. @return This Configuration @see #Coalesce() */ public Configuration Duplicate_Parameter_Action ( int action ) { if (action == 0) Duplicate_Parameter_Action = THROW_ON_DUPLICATE_PARAMETERS; else if (action > 0) Duplicate_Parameter_Action = PREFER_FIRST_PARAMETER; else if (action < 0) Duplicate_Parameter_Action = PREFER_LAST_PARAMETER; return this; } /** Gets the duplicate parameter action during coalescing.

@return The default parameter action. @see #Duplicate_Parameter_Action(int) */ public int Duplicate_Parameter_Action () {return Duplicate_Parameter_Action;} /** Coalesce all Aggregate parameters of the same name together.

Each Aggregate in the current list is compared, by name, against all other Aggregates in the list. When a duplicate name is found the parameters it contains are added to the end of the first parameter in the list having the same name. Finally each Aggregate is recursively coalesced in the same manner.

In addition, all Assignment (i.e. non-Aggregate) parameters in each Aggregate are checked for duplicates and moved to the front of the list of parameters.

The action taken when duplicate Assignment Parameter is encountered depends on the current {@link #Duplicate_Parameter_Action(int) Duplicate_Parameter_Action}. Byt default the actions is to throw a Configuration_Exception.

@throws Configuration_Exception If there was a duplicate Assignment parameter and the Duplicate_Parameter_Action is THROW_ON_DUPLICATE_PARAMETERS, or there was a problem moving parameters around in the list. */ public void Coalesce () throws Configuration_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Coalesce"); coalesce (this); if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Coalesce"); } private synchronized void coalesce ( Parameter parameters ) throws Configuration_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> coalesce: " + parameters.Path_Name ()); Vector list; try { list = parameters.List (); if (list == null) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< coalesce: " + parameters.Path_Name ()); return; } } catch (PVL_Exception exception) { /* No list */ if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< coalesce: " + parameters.Path_Name ()); return; } int this_index, that_index, total_parameters = list.size (); if (total_parameters == 0) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< coalesce: " + parameters.Path_Name ()); return; } Parameter this_parameter, that_parameter, /* Accumulation of Assignment Parameters in the current Parameter list. It starts as an Assignment as a convenient flag that it is empty. */ assignments = new Parameter (); for (this_index = 0; this_index < total_parameters; this_index++) { this_parameter = (Parameter)list.elementAt (this_index); if (this_parameter.Is_Aggregate ()) { for (that_index = this_index + 1; that_index < total_parameters; that_index++) { // Check the remainder of the list for matching Aggregates. that_parameter = (Parameter)list.elementAt (that_index); if (that_parameter.Is_Aggregate () && (Case_Sensitive ? that_parameter.Name () .equals (this_parameter.Name ()) : that_parameter.Name () .equalsIgnoreCase (this_parameter.Name ()))) { try { this_parameter.Add (parameters.Remove (that_index--).List ()); total_parameters--; } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "During Coalesce of \"" + parameters.Name () + '"' + NL + " unable to Add to \"" + this_parameter.Name () + "\" the parameter List from \"" + that_parameter.Name () + "\"." + exception_message (exception), exception ); } } } // Coalesce the coalesced Aggregate. coalesce (this_parameter); } else { // Check for a duplicate Assignment parameter. if (assignments.Is_Aggregate () && (that_parameter = assignments.Find (this_parameter.Name (), Case_Sensitive)) != null) { switch (Duplicate_Parameter_Action) { case PREFER_FIRST_PARAMETER: // Drop the duplicate from the parameters list. parameters.Remove (this_index--); total_parameters--; // Move on to the next parameter in the list. continue; case THROW_ON_DUPLICATE_PARAMETERS: String this_value = null, that_value = null; try { this_value = this_parameter.Value ().toString (); that_value = that_parameter.Value ().toString (); } catch (PVL_Exception exception) {/* Shouldn't happen. */} throw new Configuration_Exception ( ID + NL + "During Coalesce of \"" + parameters.Name () + '"' + NL + " duplicate parameter name - \"" + this_parameter.Path_Name () + "\";" + NL + " original value - " + that_value + NL + " duplicate value - " + this_value ); case PREFER_LAST_PARAMETER: // Drop the assignments list duplicate. assignments.Remove (that_parameter); // Accumulate the parameters list duplicate. } } try { // Move the the Assignment Parameter into the accumulated list. assignments.Add (parameters.Remove (this_index--)); total_parameters--; } catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "During Coalesce of \"" + parameters.Name () + '"' + NL + " while accumulating Assignment parameter \"" + this_parameter.Name () + "\"." + NL + exception_message (exception), exception ); } } } if (assignments.Is_Aggregate ()) { // Insert the accumulated Assignments before the first Aggregate. try {parameters.Insert (assignments.List (), 0);} catch (PVL_Exception exception) { throw new Configuration_Exception ( ID + NL + "During Coalesce of \"" + parameters.Name () + '"' + NL + " unable to move Assignment parameters to the front." + exception_message (exception), exception ); } } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< coalesce: " + parameters.Path_Name ()); } /*============================================================================== Helpers */ /** Get an exception message. @param exception The Exception from which to obtain a message. @return A String that begins with a NL sequence followed by the message from the exception if the exception is not null and its message is neither null nor empty. */ static private String exception_message ( Throwable exception ) { String message = NL; if (exception != null) { String exception_message = exception.getMessage (); if (exception_message != null && exception_message.length () != 0) message += exception_message; } return message; } /*============================================================================== Application */ /** Assemble and report on the contents of a configuration.

The command line syntax is described by the {@link #Usage() Usage} method.

Exit status:

0 - Success
1 - Command line syntax problem
2 - Unable to assemble the configuration
3 - The source file could not be found
*/ public static void main ( String[] arguments ) { if (arguments.length == 0) Usage (); Configuration configuration = null; String source = null; Vector find_pathnames = new Vector (); boolean quiet = false, case_sensitive = false, pattern_match = false; for (int count = 0; count < arguments.length; count++) { if (arguments[count].length () > 1 && arguments[count].charAt (0) == '-') { switch (arguments[count].charAt (1)) { case 'Q': // Quiet case 'q': quiet = true; case 'F': // Find case 'f': if (++count == arguments.length || arguments[count].charAt (0) == '-') { System.out.println ("Missing parameter pathname."); Usage (); } find_pathnames.add (arguments[count]); break; case 'C': // Case_sensitive case 'c': case_sensitive = true; pattern_match = false; break; case 'N': // Not_case_sensitive case 'n': case_sensitive = false; break; case 'P': // Pattern case 'p': pattern_match = true; case_sensitive = false; break; case 'V': // Version case 'v': System.out.println (ID); System.exit (0); default: System.out.println ("Unknown option: " + arguments[count]); case 'H': // Help case 'h': Usage (); } } else { if (source != null) { System.out.println ("More than one Configuration source specified -" + NL +" First source: " + source + NL +" Second source: " + arguments[count]); Usage (); } source = arguments[count]; } } if (source == null) { System.out.println ("No Configuration source specified."); Usage (); } try { Include_Tracing_Default = true; try { if (source.equals ("-")) configuration = new Configuration (System.in); else configuration = new Configuration (source); } catch (IllegalArgumentException exception) { System.err.println ("Could not find the configuration source file: " + source); System.exit (3); } configuration.Add_Environment (); if (find_pathnames.isEmpty ()) System.out.println (configuration.Description ()); else { Iterator pathnames = find_pathnames.iterator (); String parent_pathname, comments; Parameter parameter, test_parameter = new Parameter (); Selection selection = new Selection (); selection.Name (true); selection.Specific (case_sensitive); selection.Pattern_Match (pattern_match); Lister lister = new Lister ().Indent_Arrays (false); while (pathnames.hasNext ()) { test_parameter.Name ((String)pathnames.next ()); parameter = null; while ((parameter = configuration.Find (test_parameter, selection, parameter)) != null) { comments = parameter.Comments (); parameter.Comments (null); try { if (! quiet && comments != null) lister.Write_Comments (comments); parent_pathname = Parent_Pathname (Pathname (parameter)); if (parent_pathname != null) System.out.print (parent_pathname); System.out.print (Path_Delimiter ()); parameter.Write (lister); } catch (PVL_Exception exception) { System.err.println ("PVL_Exception while listing parameter \"" + parameter.Path_Name () + '"' + NL + exception.getMessage ()); } catch (IOException exception) { System.err.println ("IOException while listing parameter \"" + parameter.Path_Name () + '"' + NL + exception.getMessage ()); } } } } } catch (Configuration_Exception exception) { System.err.println (NL +"Configuration Exception:" + NL + exception.getMessage ()); System.exit (2); } System.exit (0); } /** Prints the command line usage syntax.

Usage: Configuration <Options> <source>
  Options -
    -Query | -Find <pathname>
    [-Not_]Case_sensitive
    -Pattern

Only the source of the configuration parameters is required. This may be a filename or a URL.

The entire configuration, with all included configurations, will be listed unless a specific parameter pathname is specified with the -query or -find option. The only difference between -query and -find is that the former never lists parameter comments. A parameter pathname may be absolute or relative. All parameters having the pathname will be listed. Pathname matching is not case sensitive unless the -case_sensitive option is specified. If -pattern is specified the pathname is taken to be a regular expression {@link java.util.regex.Pattern Pattern}. The -query/-find option may be used more than once to find more than one parameter.

N.B.This method always results in a System.exit with a status of 1. */ public static void Usage () { System.out.print (ID + NL +"Usage: Configuration " + NL +" Options -" + NL +" -Query | -Find " + NL +" -[Not_]Case_sensitive" + NL +" -Pattern" + NL +" -Help" + NL +" -Version" + NL ); System.exit (1); } } // End of class pirl-2.3.8/PIRL/Configuration/package.html0000644000175000017500000000666711742733240020224 0ustar mathieumathieu A Configuration provides a convenient means of maintaining a Parameter list used for configuration management.

The Configuration parameters may be assembled from any source of Parameter Value Language (PVL) statements - a file, URL, or system resource such as a jar file - or an existing Parameter object. Any "@Include" parameter found is recursively processed as a source of PVL statements to replace the "@Include" parameter. A set of basic default parameters is always provided, and all environment variables are added to the Configuration. A new Configuration has its parameters coalesced so that groups of parameters of the same name result in a single group, and duplicate parameters are removed (or throw an exception) on either a first or last preference basis.

Methods are provided to get parameter values by name. The name may be a simple name or an absolute or relative pathname. The name may be treated as case sensitive or not. When a parameter value is the name of another parameter the first parameter may be treated as a link to the value of the second parameter. When a named parameter can not be found, a search up the Parameter hierarchy may be automatically done for a parameter of the specified name; this allows default values to be provided at a higher level for all parameter groups at a lower level. All parameter values are returned as Strings. The value of a parameter may be a single value or an Array of values; if desired, only the first value of an Array will be obtained. An entire Group of parameters may be obtained as a sub-Configuration, with all parameters from higher levels than the group automatically incorporated into the new Configuration.

Methods are provided to set parameter values by name using the String representation of any Object. The setting of the new value may be conditional on a parameter of the specified name not already being present in the Configuration, and all occurances of a parameter name may be set. These methods indicate when the value of an existing parameter was changed as opposed to creating a new parameter. Parameter values may also be set using the name and value of another Parameter or from an array of name, value String pairs.

Methods are provided to remove parameters by name. Either a specific parameter or all parameters with a matching name may be removed.

Because a Configuration is a subclass of Parameter, it may be manipulated using any of the Parameter methods. @see PIRL.PVL pirl-2.3.8/PIRL/Configuration/Configuration0000755000175000017500000000405511652356205020467 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the Java Configuration application. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath or provided as the value of an environment # variable having the names listed here: # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # # CVS ID: Configuration,v 1.3 2011/10/27 22:34:45 castalia Exp $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Configuration.Configuration", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Configuration/Configuration_Exception.java0000644000175000017500000000627411742733240023425 0ustar mathieumathieu/* Configuration_Exception PIRL CVS ID: Configuration_Exception.java,v 1.7 2012/04/16 06:05:20 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Configuration; /** The excpetion thrown from the PIRL.Configuration package.

@author Bradford Castalia, Christian Schaller - UA/PIRL @version 1.7 */ public class Configuration_Exception extends java.lang.Exception { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Configuration.Configuration_Exception (1.7 2012/04/16 06:05:20)"; /*============================================================================== Constructors */ /** Constructs a Configuration_Exception with a message and a cause.

N.B.: Once a cause is set it can not be changed. Therefore, though it is allowed to set the cause to null, the Configuration_Exception will not do this to avoid the situation of not being able to replace a null cause with a valid cause. Use the {@link Throwable#initCause(Throwable)} method to provide a cause after the Configuration_Exception has been constructed without one.

@param message The exception's message String. If null the {@link #ID class identification} will be used. @param cause The exception's Throwable cause. If null no cause is set in the exception object. */ public Configuration_Exception ( String message, Throwable cause ) { super ((message == null) ? ID : message); if (cause != null) initCause (cause); } /** Constructs a Configuration_Exception having a cause.

The exception message will be Configuration_Exception class ID.

@param cause The exception's Throwable cause. If null no cause is set in the exception object. @see #Configuration_Exception(String, Throwable) */ public Configuration_Exception ( Throwable cause ) {this (null, cause);} /** Constructs a Configuration_Exception with a message.

@param message The exception's message String (may be null). @see #Configuration_Exception(String, Throwable) */ public Configuration_Exception ( String message ) {this (message, null);} /** Constructs a Configuration_Exception.

The exception will have the ID of this class as its message.

@see #Configuration_Exception(String, Throwable) */ public Configuration_Exception () {this (null, null);} } // End of class pirl-2.3.8/PIRL/Configuration/tests/0000755000175000017500000000000012052546520017064 5ustar mathieumathieupirl-2.3.8/PIRL/Configuration/tests/Makefile0000644000175000017500000000036311033332335020521 0ustar mathieumathieu# Makefile for Java classes JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = test_Configuration.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Configuration/tests/test_Configuration.java0000644000175000017500000003374711742733240023615 0ustar mathieumathieu/* test_Configuration PIRL CVS ID: test_Configuration.java,v 1.9 2012/04/16 06:05:20 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import java.net.URL; import java.util.*; import java.io.*; import PIRL.PVL.*; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Utilities.Checker; public class test_Configuration { /** The name of the test configuration file we'll be using. This file will be created during the testing, in the directory from which the testing is conducted. */ private static final String CONFIGURATION_FILENAME = "test_Configuration.conf"; public static void main ( String[] arguments ) { System.out.println ("*** test_Configuration: " + Configuration.ID); Configuration config_A = null, config_B = null; String expected, path, value; boolean condition; int status = 0; Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; try { // Constructors: System.out.println ("=== Constructors:"); // Construct from a Parameter. String config_A_list = "P0 = V0\n" + "absolute_link = /G0/G1\n" + "relative_link = G1\n" + "GROUP = G0\n" + " P1a = V1a\n" + " P1b = V1b\n" + " P1c = V1c\n" + " GROUP = G1\n" + " P2 = V2\n" + " END_GROUP\n" + "END_GROUP\n" + "END\n"; Parameter parameter = new Parameter ( "Test_Configuration", (Parser)(new Parser (config_A_list) .Strict (false) .Crosshatch_Comments (true) .Filter_Input (false)) ); config_A = new Configuration (parameter); checker.Check ("new Configuration (Parameter)", config_A_list, Trimmed_Description (config_A)); // Copy the Configuration; change the case sensitivity. condition = ! config_A.Case_Sensitive (); config_A.Case_Sensitive (condition); config_B = new Configuration (config_A); checker.Check ("new Configuration (Configuration)", config_A_list, Trimmed_Description (config_B)); checker.Check ("Case sensitivity copied", condition, config_B.Case_Sensitive ()); // Construct from a file. File file = new File (CONFIGURATION_FILENAME); FileWriter filewriter = new FileWriter (file); filewriter.write (config_A_list); filewriter.close (); config_A = new Configuration (CONFIGURATION_FILENAME); checker.Check ("new Configuration (\"" + CONFIGURATION_FILENAME + "\")", config_A_list, Trimmed_Description (config_A)); // Construct from a class relative system resource. File moved_file = new File (".." + File.separator + CONFIGURATION_FILENAME); if (file.renameTo (moved_file)) { Configuration.Relative_to_Class (Configuration.class); config_A = new Configuration (CONFIGURATION_FILENAME); checker.Check ("new Configuration (\"" + CONFIGURATION_FILENAME + "\"), Relative_to_Class " + Configuration.class, config_A_list, Trimmed_Description (config_A)); Configuration.Relative_to_Class (null); moved_file.delete (); filewriter = new FileWriter (file); filewriter.write (config_A_list); filewriter.close (); } // Construct from a URL. URL url = new URL ("file:" + CONFIGURATION_FILENAME); config_A = new Configuration (url); checker.Check ("new Configuration (URL), URL: " + url, config_A_list, Trimmed_Description (config_A)); // Set default filename and construct from it. Configuration.Default_Filename (CONFIGURATION_FILENAME); config_A = new Configuration ((String)null); checker.Check ("new Configuration (null), Default_Filename: " + Configuration.Default_Filename (), config_A_list, Trimmed_Description (config_A)); // Construct from an InputStream. String config_B_list = "Type = MySQL\n" + "Database = test\n" + "Group = MySQL\n" + " Database = MOC\n" + " Host = PIRL.LPL.Arizona.edu\n" + " Group = MOC\n" + " password = MOC_password\n" + " Group = MGSC\n" + " key = third\n" + " End_Group\n" + " End_Group\n" + " /* Alias (link) name for the MOC group */\n" + " Mars = MOC\n" + "End_Group\n" + "\n" + "Group = Text\n" + " Database = MGSC\n" + "end_group\n" + "group = mysql\n" + " password = foobar\n" + " group = moc\n" + " group = MGSC\n" + " fields = (first, second, third, fourth)\n" + " ID = product_ID\n" + " Primary_Selection = fields\n" + " end_group\n" + " table = MGSC\n" + " end_group\n" + "end_group\n" + "password = barfoo\n" + "Name = MySQL/MOC/MGSC/ID\n" + "END\n"; if (checker.Verbose) System.out.print ("=== InputStream content:\n" + config_B_list); StringBufferInputStream input_stream = new StringBufferInputStream (config_B_list); config_B = new Configuration (input_stream); // After Coalesce: config_B_list = "Type = MySQL\n" + "Database = test\n" + "password = barfoo\n" + "Name = MySQL/MOC/MGSC/ID\n" + "GROUP = MySQL\n" + " Database = MOC\n" + " Mars = MOC\n" + " password = foobar\n" + " GROUP = MOC\n" + " password = MOC_password\n" + " table = MGSC\n" + " GROUP = MGSC\n" + " key = third\n" + " fields = \n" + " (first, second, third, fourth)\n" + " ID = product_ID\n" + " Primary_Selection = fields\n" + " END_GROUP\n" + " END_GROUP\n" + "END_GROUP\n" + "GROUP = Text\n" + " Database = MGSC\n" + "END_GROUP\n" + "END\n"; checker.Check ("new Configuration (InputStream)", config_B_list, Trimmed_Description (config_B)); // Get: System.out.println ("=== Get:"); path = "/mysql/moc/mgsc/key"; config_B.Case_Sensitive (true); expected = null; checker.Check ("Case_Sensitive (true); Get_One (\"" + path + "\")", expected, config_B.Get_One (path)); config_B.Case_Sensitive (false); expected = "third"; checker.Check ("Case_Sensitive (false); Get_One (\"" + path + "\")", expected, config_B.Get_One (path)); path = "/mysql/moc/mgsc/Password"; expected = "MOC_password"; checker.Check ("Get_One (\"" + path + "\")", expected, config_B.Get_One (path)); path = "/mysql/moc/mgsc/type"; expected = "MySQL"; checker.Check ("Get_One (\"" + path + "\")", expected, config_B.Get_One (path)); path = "/mysql/moc/mgsc/NO_SUCH_PARAMETER"; expected = null; checker.Check ("Get_One (\"" + path + "\")", expected, config_B.Get_One (path)); // Set: System.out.println ("=== Set:"); path = "/mysql/moc/mgsc/key"; value = "SECOND"; checker.Check ("Set (\"" + path + "\", \"" + value + "\") replaced existing value", true, config_B.Set (path, value)); checker.Check ("Set (\"" + path + "\", \"" + value + "\") value confirmation", value, config_B.Get_One (path)); path = "/mysql/moc/mgsc/INDEX"; value = "Index"; checker.Check ("Set (\"" + path + "\", \" " + value + "\") replaced existing value", false, config_B.Set (path, value)); checker.Check ("Set (\"" + path + "\", \"" + value + "\") value confirmation", value, config_B.Get_One (path)); path = "/mysql/moc/NEW/KEY"; value = "Key"; checker.Check ("Set_Conditionally (\"" + path + "\", \" " + value + "\") new parameter", false, config_B.Set_Conditionally (path, value)); checker.Check ("Set_Conditionally (\"" + path + "\", \"" + value + "\") value confirmation", value, config_B.Get_One (path)); expected = value; value = "KEY_REPLACED"; checker.Check ("Set_Conditionally (\"" + path + "\", \" " + value + "\") existing parameter", true, config_B.Set_Conditionally (path, value)); checker.Check ("Set_Conditionally (\"" + path + "\", \"" + value + "\") value confirmation", expected, config_B.Get_One (path)); // Links: System.out.println ("=== Links:"); expected = "product_ID"; checker.Check ("Get_Linked_One (\"ID\")", expected, config_B.Get_Linked_One ("ID")); checker.Check ("Get_Linked_One (\"Name\")", expected, config_B.Get_Linked_One ("Name")); expected = "[first, second, third, fourth]"; checker.Check ("Get_Linked (\"Primary_Selection\")", expected, config_B.Get_Linked ("Primary_Selection").toString ()); // Group: System.out.println ("=== Groups:"); config_B.Add_Environment (); checker.Check ("Environment parameters group", Configuration.ENVIRONMENT, config_B.Group_Pathname (Configuration.ENVIRONMENT)); if (checker.Verbose) { Configuration environment = config_B.Group (Configuration.ENVIRONMENT); if (environment == null) System.out.println ("No " + Configuration.ENVIRONMENT + " found."); else System.out.println (environment.Description ()); } config_B_list = "password = MOC_password\n" + "table = MGSC\n" + "Alias = MOC\n" + "Database = MOC\n" + "Mars = MOC\n" + "Type = MySQL\n" + "Name = MySQL/MOC/MGSC/ID\n" + "GROUP = MGSC\n" + " key = SECOND\n" + " fields = \n" + " (first, second, third, fourth)\n" + " ID = product_ID\n" + " Primary_Selection = fields\n" + " INDEX = Index\n" + "END_GROUP\n" + "GROUP = NEW\n" + " KEY = Key\n" + "END_GROUP\n" + "END\n"; config_B = config_B.Group ("Mars"); checker.Check ("Group (\"Mars\")", config_B_list, Trimmed_Description (config_B)); // Groups & Links: System.out.println ("=== Groups w/Links:"); // Group_Pathname. expected = "/G0/G1"; checker.Check ("Group_Pathname (\"G1\")", expected, config_A.Group_Pathname ("G1")); checker.Check ("Group_Pathname (\"absolute_link\")", expected, config_A.Group_Pathname ("absolute_link")); checker.Check ("Group_Pathname (\"relative_link\")", expected, config_A.Group_Pathname ("relative_link")); // Include: System.out.println ("=== Include:"); Configuration.Include_Default = false; path = "@INCLUDE"; value = CONFIGURATION_FILENAME; checker.Check ("Set (\"" + path + "\", \"" + value + "\")", false, config_B.Set (path, value)); checker.Check ("Set (\"" + path + "\", \"" + value + "\") value confirmation", value, config_B.Get_One (path)); // Place the include at the end of the assignment list. config_B.Coalesce (); // Copy the template. config_A = new Configuration (config_B); config_B.Include (); config_B_list = config_B_list.replaceAll ("/ID\\n", "/ID\n"+ config_A_list.replaceAll ("END\\n", "")); checker.Check ("Include", config_B_list, Trimmed_Description (config_B)); // Resolved reference config_B = new Configuration (config_A); path = "@INCLUDE"; value = "${INDEX}"; checker.Check ("Set (\"" + path + "\", \"" + value + "\")", true, config_B.Set (path, value)); path = "INDEX"; value = CONFIGURATION_FILENAME; checker.Check ("Set (\"" + path + "\", \"" + value + "\")", true, config_B.Set (path, value)); config_B.Include (); config_B_list = config_B_list.replaceAll ("Index", CONFIGURATION_FILENAME); checker.Check ("Include with resolved reference", config_B_list, Trimmed_Description (config_B)); // Recursive include. config_A_list = "@INCLUDE = " + CONFIGURATION_FILENAME + '\n' + config_A_list; filewriter = new FileWriter (new File (CONFIGURATION_FILENAME)); filewriter.write (config_A_list); filewriter.close (); try { config_A.Include (); expected = "Configuration_Exception - recursive include"; value = Trimmed_Description (config_A); } catch (Configuration_Exception exception) { expected = exception.getMessage (); value = expected; } checker.Check ("Recursive include exception", expected, value); } catch (Exception exception) { status = 1; System.out.println ("\n!!! Unexpected exception:\n" + exception + '\n' + exception.getMessage ()); } if (status == 0 && checker.Checks_Total == checker.Checks_Passed) new File (CONFIGURATION_FILENAME).delete (); System.out.println ("\n" + "Checks: " + checker.Checks_Total + '\n' + "Passed: " + checker.Checks_Passed); System.exit (status); } /** Trims a Configuration listing of boilerplate.

The Configuration has its default "User", "Host", and "Classpath" parameters removed and its Name is changed to the Parser.CONTAINER_NAME so the enclosing Group will not be included in the listing. A Listing of the Configuration is written to a String. Then the original Name is restored.

@param configuration The Configuration to be listed. @return The String containing the listing. If an Exception occurs while generating the listing, the exception message will be returned. If the lister encounters a Warning condition, the warning message will be appended to the listing. If the configuration is null, null will be returned. */ private static String Trimmed_Description ( Configuration configuration ) { if (configuration == null) return null; // Remove DEFAULTS. configuration.Remove ("User"); configuration.Remove ("Host"); configuration.Remove ("Classpath"); // Remove Environment variables. configuration.Remove_Group (Configuration.ENVIRONMENT); StringWriter description = new StringWriter (); Lister lister = new Lister (description).Strict (false); // Prevent the top level from being written. String name = configuration.Name (); configuration.Name (Parser.CONTAINER_NAME); try {lister.Write (configuration);} catch (Exception exception) { // Restore the original name. configuration.Name (name); return exception.getMessage (); } // Restore the original name. configuration.Name (name); if (lister.Warning () != null) description.getBuffer () .append ("\nWarning:\n") .append (lister.Warning ().getMessage ()); return description.toString (); } } pirl-2.3.8/PIRL/Conductor/0000755000175000017500000000000012052546525015060 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Create_Pipeline0000755000175000017500000002271211742733132020037 0ustar mathieumathieu#!/usr/bin/perl =pod =head1 NAME B Create a Conductor pipeline table set in a MySQL database. =head1 SYNOPSIS Create_Pipeline | Create_[EBE.]EBE [-Catalog EBE[.EBE]] [[-Pipeline] [EBE.]EBE] =head1 OPTIONS AND ARGUMENTS Both a catalog and pipeline name are required. Either or both may be specified in the command name, the -Catalog name or the -Pipeline name arguments. A name containing a period character ('.') is split into a catalog name from the part preceeding the period and a pipeline name from the part following the period. If the command name contains an underbar character ('_') that is followed by a non-zero length word that is not "Pipeline" then that word is taken to be the pipeline name (or catalog.pipeline if it contains a period). A name without a period is the pipeline name unless it occurs with the -Catalog argument in which case it is the catalog name. =over =item -Catalog EBE[.EBE] The name of the database catalog where the pipeline tables are to be created. The catalog name may be qualified by the EBE name, following a '.' delimiter. Default: The EBE name in the -Pipeline name or command name, if present, in that order. =item [-Pipeline] [EBE.]EBE The prefix name of the pipline tables. The pipeline name may be qualified by the EBE name, preceeding a '.' delimiter. Default: The EBE name in the -Catalog name or command name, if present, in that order. Note: A command line argument that is not preceeded by a switch (with a '-' prefix) is treated as if it was preceeded by the -Pipeline switch. =item -Help The man page for this procedure is listed. =back =head1 DESCRIPTION A pair of pipeline tables will be created in the database catalog. The mysql command line utility will be used to do this. The catalog (a.k.a. "database") will be created if it does not exist. Then the pipeline tables will be created in the catalog if they do not already exist. Note that existing tables are never replaced or modified; if an existing table with the expected pipeline name does not have the expected field definitions these will not be changed. The EBE_Sources table will contain the following fields: Field Name Field Type ------------------ --------------------------------------------- Source_Number INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY Source_Pathname TEXT NOT NULL Source_ID VARCHAR(64) BINARY Conductor_ID VARCHAR(64) Status TEXT Log_Pathname TEXT Last_Update TIMESTAMP The EBE_Procedures table will contain the following fields: Field Name Field Type ------------------ ---------------------------------------------- Sequence FLOAT NOT NULL PRIMARY KEY Description TEXT Command_Line TEXT NOT NULL Success_Status TEXT Success_Message TEXT Time_Limit TEXT On_Failure TEXT Last_Update TIMESTAMP Both tables will be TYPE InnoDB. =head1 Exit Status =over =item E<48> - Success The database table insert(s) completed successfully. =item E<49> - Bad command line syntax A command line syntax usage message will be provided. =item E<50> - Create failed There was a problem creating the database tables. This usually means that the user does not have the required database permissions to create the catalog and/or tables. =back =head1 Author Bradford Castalia, UA/PIRL =head1 Copyright Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . =head1 Version 1.14 2012/04/16 06:04:10 =cut # PIRL CVS ID: Create_Pipeline,v 1.14 2012/04/16 06:04:10 castalia Exp #=============================================================================== $CVS_ID = 'Create_Pipeline (1.14 2012/04/16 06:04:10)'; ($Command_Name = $0) =~ s|.*/(\w+)$|$1|; print "$CVS_ID\n"; # Database client for submitting SQL statements. @SQL_Tool = ('mysql', '-e'); $Sources_Fields = q( ( Source_Number INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, Source_Pathname TEXT NOT NULL, Source_ID VARCHAR(64) BINARY, Conductor_ID VARCHAR(64), Status TEXT, Log_Pathname TEXT, Last_Update TIMESTAMP ) TYPE=InnoDB ); $Procedures_Fields = q( ( Sequence FLOAT NOT NULL PRIMARY KEY, Description TEXT, Command_Line TEXT NOT NULL, Success_Status TEXT, Success_Message TEXT, Time_Limit TEXT, On_Failure TEXT, Last_Update TIMESTAMP ) TYPE=InnoDB ); # Exit status values: $SUCCESS = 0; $BAD_SYNTAX = 1; $CREATE_FAILED = 2; #------------------------------------------------------------------------------- # Command line arguments: use Pod::Usage; while ($option = shift @ARGV) { if ($option =~ /^-[Pp]/) { # Pipeline. if (! @ARGV || $ARGV[0] =~ /^-/) { Missing_Pipeline: pod2usage ( -message => "$Command_Name: Missing pipeline name.\n", -verbose => 0, -exitval => BAD_SYNTAX ); } $option = shift @ARGV; Pipeline_Name: $Pipeline = $option unless ($Pipeline = pipeline_name ($option)); # Check for catalog name. $Catalog = catalog_name ($option) unless $Catalog; next; } if ($option =~ /^-[Cc]/) { # Catalog. if (! @ARGV || $ARGV[0] =~ /^-/) { Missing_Catalog: pod2usage ( -message => "$Command_Name: Missing database catalog name.\n", -verbose => 0, -exitval => $BAD_SYNTAX ); } $Catalog = $option unless ($Catalog = catalog_name ($option = shift @ARGV)); # Check for pipeline name. $Pipeline = pipeline_name ($option) unless $Pipeline; next; } # Help. pod2usage ( -verbose => 2, -exitval => 0 ) if ($option =~ /^-[Hh]/); pod2usage ( -message => "$Command_Name: Unknown option \"$option\"\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($option =~ /^-/); goto Pipeline_Name; } # Ensure that both a catalog and pipeline name were provided. if (! $Pipeline) { # Check the command name for the pipeline name. ($Create, $pipeline) = split (/_/, $Command_Name, 2); goto Missing_Pipeline if ($pipeline eq "Pipeline" || $pipeline eq ""); $Pipeline = $pipeline if (! ($Pipeline = pipeline_name ($pipeline))); # Check for catalog name. $Catalog = catalog_name ($pipeline) if (! $Catalog); } goto Missing_Catalog if (! $Catalog); #------------------------------------------------------------------------------- # Catalog print "==> Creating the $Catalog database catalog,\n", " if it doesn't already exist.\n"; &Database_Operation ("CREATE DATABASE IF NOT EXISTS $Catalog"); #------------------------------------------------------------------------------- # Sources table print "==> Creating the ${Catalog}.${Pipeline}_Sources table,\n", " if it doesn't already exist,\n", " with fields -", $Sources_Fields; &Database_Operation ("CREATE TABLE IF NOT EXISTS $Catalog.$Pipeline"."_Sources $Sources_Fields"); #------------------------------------------------------------------------------- # Procedures table print "==> Creating the ${Catalog}.${Pipeline}_Procedures table,\n", " if it doesn't already exist,\n", " with fields -", $Procedures_Fields; &Database_Operation ("CREATE TABLE IF NOT EXISTS $Catalog.$Pipeline"."_Procedures $Procedures_Fields"); exit (0); #------------------------------------------------------------------------------- sub catalog_name { my ($name) = @_; return (($index = index ($name, '.')) > 0) ? substr ($name, 0, $index) : undef; } sub pipeline_name { my ($name) = @_; return (($index = index ($name, '.')) >= 0 && $index < (length ($name) - 1)) ? substr ($name, $index + 1) : undef; } sub Database_Operation { my ($SQL_statement) = @_; # Replace NL and tabs and compress down whitespace. $SQL_statement =~ tr/\n\t / /s; # Trim off leading and trailing whitespace. $SQL_statement =~ s|^\s*(.*?)\s*$|$1|; push @SQL_Tool, $SQL_statement; print "++> Database_Operation:\n@SQL_Tool\n" if ($Verbose > 1); $Exit_Status = system @SQL_Tool; if ($Exit_Status == -1) { print STDERR "$Command_Name: The command could not be executed!\n"; "@SQL_Tool\n"; exit ($CREATE_FAILED); } if ($Exit_Status >> 8) { print STDERR "$Command_Name: The command failed - exit status $Exit_Status.\n", " You may not have the database privileges to do this.\n", "@SQL_Tool\n"; exit ($CREATE_FAILED); } pop @SQL_Tool; return $Exit_Status; } pirl-2.3.8/PIRL/Conductor/Maestro/0000755000175000017500000000000012052546524016471 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Matrix_Model.java0000644000175000017500000003722611742733133023751 0ustar mathieumathieu/* Conductor_Matrix_Model PIRL CVS ID: Conductor_Matrix_Model.java,v 1.15 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Messenger.Message; import javax.swing.table.AbstractTableModel; import javax.swing.event.TableModelListener; import javax.swing.event.TableModelEvent; import java.util.Vector; /** A Conductor_Matrix_Model contains the data for a matrix of the number of Conductors at each Theater location.

Each column of the matrix table is for a theater location. Each row is for a Conductor name. Each cell contains the number of Conductors having the row name operating on the theater location of the column name.

@author Bradford Castalia, UA/PIRL @version 1.15 */ public class Conductor_Matrix_Model extends AbstractTableModel implements TableModelListener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Matrix_Model (1.15 2012/04/16 06:04:11)"; public static final String CONDUCTORS_COLUMN_NAME = "Conductors", TOTALS_NAME = "Totals"; public static final int RUN_TO_WAIT_STATE = Conductor_Table.RUN_TO_WAIT_STATE, RUNNING_STATE = Conductor_Table.RUNNING_STATE, POLLING_STATE = Conductor_Table.POLLING_STATE, WAITING_STATE = Conductor_Table.WAITING_STATE, HALTED_STATE = Conductor_Table.HALTED_STATE; private Vector Theater_Names = new Vector (), Theater_Locations = new Vector (), Conductor_Names = new Vector (); private Vector> Counts = new Vector> (); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ADD = 1 << 1, DEBUG_REMOVE = 1 << 2, DEBUG_INCREMENTORS = 1 << 3, DEBUG_REBUILD = 1 << 4, DEBUG_LISTENER = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Conductor_Matrix_Model ( Conductor_Table_Model table_model ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Conductor_Matrix_Model"); table_model.Add_State_Change_Listener (this); } /*============================================================================== AbstractTableModel */ public int findColumn ( String theater_location ) { int column; if ((column = Theater_Names.indexOf (theater_location)) >= 0 || (column = Theater_Locations.indexOf (theater_location)) >= 0) column++;; return column; } public int findRow ( String conductor_name ) {return Conductor_Names.indexOf (conductor_name);} /*============================================================================== TableModel */ public Class getColumnClass ( int column ) { if (column == 0) return String.class; else if (column <= Theater_Locations.size ()) return Count.class; return Object.class; } public int getColumnCount () {return Theater_Locations.size () + 1;} public String getColumnName ( int column ) { if (column == 0) return CONDUCTORS_COLUMN_NAME; if (--column < Theater_Names.size ()) return Theater_Names.get (column); return null; } public int getRowCount () {return Conductor_Names.size ();} public Object getValueAt ( int row, int column ) { try { if (column == 0) return Conductor_Names.get (row); else return Counts.get (row).get (--column); } catch (IndexOutOfBoundsException exception) {} return null; } /*============================================================================== Accessors */ /** Get the table column index for a Conductor identity.

@return The table column index for the Conductor identity. This will be -1 if the identity is null, empty, or has a {@link Conductor_Table_Model#Theater_Location(Message) Theater location} not present in this table. */ public int Column ( Message identity ) {return findColumn (Conductor_Table_Model.Theater_Location (identity));} /** Get the table row index for a Conductor identity.

@return The table row index for the Conductor identity. This will be -1 if the identity is null, empty, or has a {@link Conductor_Table_Model#Conductor_Name(Message) Conductor name} not present in this table. */ public int Row ( Message identity ) {return findRow (Conductor_Table_Model.Conductor_Name (identity));} public Count Count ( int row, int column ) { if (column > 0 && column <= getColumnCount () && row >= 0 && row < getRowCount ()) return Count_at (row, column); return null; } public Count Count ( Message identity ) { int column = Column (identity); if (column > 0) { int row = Row (identity); if (row >= 0) return Count_at (row, column); } return null; } private Count Count_at ( int row, int column ) {return Counts.get (row).get (--column);} public int Total ( int row, int column ) { if (column > 0 && column <= getColumnCount () && row >= 0 && row < getRowCount ()) return Count_at (row, column).Counts[TOTAL]; return -1; } public int Total ( String theater_location, String conductor_name ) { int column = findColumn (conductor_name); if (column > 0) { int row = findRow (theater_location); if (row >= 0) return Count_at (row, column).Counts[TOTAL]; } return -1; } public int Total ( Message identity ) { int column = Column (identity); if (column > 0) { int row = Row (identity); if (row >= 0) return Count_at (row, column).Counts[TOTAL]; } return -1; } protected Conductor_Matrix_Model Add ( Message identity ) { if ((DEBUG & DEBUG_ADD) != 0) System.out.println (">>> Conductor_Matrix_Model.Add -" + identity); if (identity != null) { String theater_location, conductor_name; if ((theater_location = Conductor_Table_Model.Theater_Location (identity)) != null && (conductor_name = Conductor_Table_Model.Conductor_Name (identity)) != null) { int column = findColumn (theater_location), row = findRow (conductor_name); Add_to (column, theater_location, row, conductor_name, Conductor_Table_Model.Processing_State (identity)); // Update the table view. if (column < 0) fireTableStructureChanged (); else if (row < 0) fireTableRowsInserted (row, row); else fireTableCellUpdated (row, column); } } if ((DEBUG & DEBUG_ADD) != 0) System.out.println ("<<< Conductor_Matrix_Model.Add"); return this; } protected void Add_to ( int column, String theater_location, int row, String conductor_name, int processing_state ) { if ((DEBUG & DEBUG_ADD) != 0) System.out.println (">>> Add_to:" + NL +" Column " + column + " - " + theater_location + NL +" Row " + row + " - " + conductor_name + NL +" State = " + processing_state); boolean changed = false; if (column < 0) { // Add the new column. column = getColumnCount (); if ((DEBUG & DEBUG_ADD) != 0) System.out.println (" Adding column " + column); for (int rows = getRowCount (), index = 0; index < rows; index++) Counts.get (index).add (new Count ()); changed = Add_to_Theater_Names (theater_location); } if (row < 0) { // Add the new row. row = getRowCount (); if ((DEBUG & DEBUG_ADD) != 0) System.out.println (" Adding row " + row); Vector row_vector = new Vector (Theater_Locations.size ()); for (int columns = getColumnCount () - 1, index = 0; index < columns; index++) row_vector.add (new Count ()); Counts.add (row_vector); Conductor_Names.add (conductor_name); } // Increment the count. if ((DEBUG & DEBUG_ADD) != 0) System.out.println (" Increment count at row " + row + ", column " + column); Increment (Count_at (row, column), processing_state); if ((DEBUG & DEBUG_ADD) != 0) System.out.println ("<<< Add_to"); } private boolean Add_to_Theater_Names ( String theater_location ) { Theater_Locations.add (Theater.Full_Location (theater_location)); String name = Theater.Location (theater_location); // Check for duplicate Theater_Names entry. boolean changed = false; int index = Theater_Names.size (); while (--index >= 0) { if (Theater_Names.get (index).equals (name)) { name = Theater_Locations.lastElement (); Theater_Names.set (index, name); changed = true; break; } } Theater_Names.add (name); return changed; } protected void Rebuild ( Conductor_Table_Model model ) { if (model == null) return; if ((DEBUG & DEBUG_ADD) != 0) System.out.println (">>> Conductor_Matrix_Model.Rebuild"); Theater_Names.clear (); Theater_Locations.clear (); Conductor_Names.clear (); Counts.clear (); int model_row = 0; Message identity; while ((identity = model.Identity (model_row++)) != null) { String theater_location, conductor_name; if ((theater_location = Conductor_Table_Model.Theater_Location (identity)) != null && (conductor_name = Conductor_Table_Model.Conductor_Name (identity)) != null) Add_to ( findColumn (theater_location), theater_location, findRow (conductor_name), conductor_name, Conductor_Table_Model.Processing_State (identity) ); } // Update the table view. fireTableStructureChanged (); if ((DEBUG & DEBUG_ADD) != 0) System.out.println ("<<< Conductor_Matrix_Model.Rebuild"); } /** Adjust the model based on the removal of a Conductor identity.

If the identity can be associated with a table cell its Count values are {@link #Decrement(Count, Message) decremented}.

@param identity A Conductor identity Message. */ protected void Removing ( Message identity ) { if ((DEBUG & DEBUG_REMOVE) != 0) System.out.println (">>> Conductor_Matrix_Model.Remove -" + identity); int row, column; if ((column = Column (identity)) > 0 && (row = Row (identity)) >= 0) { if ((DEBUG & DEBUG_REMOVE) != 0) System.out.println (" row " + row + ", column " + column); Decrement (Counts.get (row).get (--column), identity); //!!! Should check for an empty row or column and adjust accordingly. fireTableCellUpdated (row, column); } if ((DEBUG & DEBUG_REMOVE) != 0) System.out.println ("<<< Conductor_Matrix_Model.Remove"); } /*============================================================================== Count */ /** Index for arrays of processing state information. */ public static final int RUNNING = 0, POLLING = 1, WAITING = 2, HALTED = 3, TOTAL = 4, // Must be last in the sequence. UNKNOWN = 5; // Must be greater than TOTAL. /** A Count contains table cell Conductor processing state count values.

A count of the number of Conductors in each processing state, including the unknown state, is maintained plus the total number of Conductors. */ public class Count { /** The Conductor processing state counts. */ public int Counts[] = {0, 0, 0, 0, 0, 0}; public String toString () {return String.valueOf (Counts[TOTAL]);} } private void Increment ( Count count, Message identity ) {Increment (count, Conductor_Table_Model.Processing_State (identity));} private void Increment ( Count count, int processing_state ) { if (count == null) return; count.Counts[TOTAL]++; switch (processing_state) { case RUN_TO_WAIT_STATE: case RUNNING_STATE: count.Counts[RUNNING]++; break; case POLLING_STATE: count.Counts[POLLING]++; break; case WAITING_STATE: count.Counts[WAITING]++; break; case HALTED_STATE: count.Counts[HALTED]++; break; default: count.Counts[UNKNOWN]++; } if ((DEBUG & DEBUG_INCREMENTORS) != 0) System.out.println (">-< Increment: " + count.Counts[RUNNING] + "R " + count.Counts[POLLING] + "P " + count.Counts[WAITING] + "W " + count.Counts[HALTED] + "H " + count.Counts[UNKNOWN] + "U " + count.Counts[TOTAL] + "T"); } private void Decrement ( Count count, Message identity ) {Decrement (count, Conductor_Table_Model.Processing_State (identity));} private void Decrement ( Count count, int processing_state ) { if (count == null) return; count.Counts[TOTAL]--; switch (processing_state) { case RUN_TO_WAIT_STATE: case RUNNING_STATE: count.Counts[RUNNING]--; break; case POLLING_STATE: count.Counts[POLLING]--; break; case WAITING_STATE: count.Counts[WAITING]--; break; case HALTED_STATE: count.Counts[HALTED]--; break; default: count.Counts[UNKNOWN]--; } if ((DEBUG & DEBUG_INCREMENTORS) != 0) System.out.println (">-< Decrement: " + count.Counts[RUNNING] + "R " + count.Counts[POLLING] + "P " + count.Counts[WAITING] + "W " + count.Counts[HALTED] + "H " + count.Counts[UNKNOWN] + "U " + count.Counts[TOTAL] + "T"); } private void Adjust ( Count count, Message identity, int previous_state ) { int processing_state = Conductor_Table_Model.Processing_State (identity); if (processing_state != previous_state); { Increment (count, processing_state); Decrement (count, previous_state); } } /*============================================================================== TableModelListener */ public void tableChanged ( TableModelEvent event ) { if (! (event instanceof Conductor_Table_Model_Event)) return; if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (">>> Conductor_Matrix_Model.tableChanged"); Conductor_Table_Model model = (Conductor_Table_Model)event.getSource (); switch (event.getType ()) { case TableModelEvent.INSERT: if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" INSERT: from " + event.getFirstRow () + " to " + event.getLastRow ()); for (int index = event.getFirstRow (); index <= event.getLastRow (); index++) Add (model.Identity (index)); break; case TableModelEvent.UPDATE: if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" UPDATE: from " + event.getFirstRow () + " to " + event.getLastRow ()); if (event.getFirstRow () == TableModelEvent.HEADER_ROW || event.getLastRow () > getRowCount ()) /* The changes to the source data model are non-specific. Since source rows may have been added or removed the entire matrix must be rebuilt. */ Rebuild (model); else { Message identity; int row, column; Count count; for (int index = event.getFirstRow (); index <= event.getLastRow (); index++) { if ((identity = model.Identity (index)) != null && (column = Column (identity)) > 0 && (row = Row (identity)) >= 0 && (count = Count_at (row, column)) != null) { Adjust (count, identity, ((Conductor_Table_Model_Event)event).Processing_State ()); fireTableCellUpdated (row, column); } } } break; case TableModelEvent.DELETE: if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" DELETE: from " + event.getFirstRow () + " to " + event.getLastRow ()); for (int index = event.getFirstRow (); index <= event.getLastRow (); index++) Removing (model.Identity (index)); break; } if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println ("<<< Conductor_Matrix_Model.tableChanged"); } } pirl-2.3.8/PIRL/Conductor/Maestro/Icons/0000755000175000017500000000000012052546524017544 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Maestro/Icons/Exclamation.png0000644000175000017500000000327311106472616022522 0ustar mathieumathieuPNG  IHDR szzsRGBbKGD pHYs  tIME ,2B;IDATXŗM]g{ν7g棵ӒiڴiQ"HpƂ0ڍIq% X7*qW7nąą)bEE iZrIfq;7D/\}9Ƀl>7=} Ƕ3) sx??39/m ,/0}4O}[c_ӟ_5Y^ۉ;At~/TJw+WO2!+6~c 0 JTE/®T=k8p59|(=8K D-opx~D`qͅ]UR_ ܸΤɥ>i"t7S!UY񢄔 w83.p)qRnKV9UvU| >ΉR * +AYqw̲W N)DTί9E>v_8sOvkƋ!asIjA.J`q18XL&+N<:cOocǻ}1DlV* `sZK gtex-F. L2MVZj-{mꟽΒD2M4IiTYre6}%KWL`#Sv,gAQ@P BlEP0œEĠG\3 30I|X j?L&岸YnR3? ]O/mdo@v©*6lHG^@<*ԃxP*Bẹrw1ٿ#QyUEUDAUQ:tIx1㜣57A*IvLO?Boy4shw$ 9sIEh_3帅+>xuGx$K⬵vԣ*wnĤ2`R^rz+g] РNsbk2L6SSBT^:gAZ'шOZ*yu#h3BDI.[Mυl $ 0ZUFm aޞߝf0> }*H*?L]>K0̕3Wzر!*3q4&~XcqKI ?M1qP,P{C6: ]=cM,cjLG>O3 YK{VhˈBmݥDm?)dE޼o_i:$0W MqdIENDB`pirl-2.3.8/PIRL/Conductor/Maestro/Icons/Open_Theater0000644000175000017500000000170411057056356022051 0ustar mathieumathieuPNG  IHDRasRGBbKGD pHYs  tIME 6)O?tEXtCommentOpen or Go buttonQIDAT8u[lSu?ۜJǺe)#[Jp'M$7 1}0>Ó>Lb4ÉHd٘ڮgݥz==tq&~~}~VtZeJZats/㘋GEX$q.=ƕ{Ȗ[mNGF^]i ) "ߥƉD?h3:d{Vt bƘOs?롫gT*:䗌~Ni,Y`9x:99ps9\.'f#|1T>;or/2+KN/sb6BpD,*d!⣼,MXkX{ K\W⮯c_Է!e_M\^4c4TgHL*!{ǹ4l!+6sG yLI7YbS0 BQRszъMAQ Af`&GMXT~("2j1MCRtemM8>qg4]NI% qm.jo|%Ob7_Sׄ(4k L̰}U' >~gwa8ڃ< aL%M]إ:씉ԫX60e V1u&_Kh}'IwR LO@m[xUbUYGgn/i_X̳#m)=?'u^B vmH٢KڻMkŅypp8(vzt*cѾIWIENDB`pirl-2.3.8/PIRL/Conductor/Maestro/Icons/Kapellmeister_Icon0000644000175000017500000001252111062676175023247 0ustar mathieumathieuPNG  IHDR00WsRGBbKGD pHYs  tIME "'h,0tEXtCommentDer Kapellmeister Johann Sebastian Bach }aIDATh޽y]y6w; gCp%(ɲM[8[+'mHn&JR8EG8Fq85lE%q'ř!9ݷoC vms9yyW,-o:LZM3}vÎmx[,'s='|mQܬ]l˹J!!2(m=7ZO [c=XVƧ<%kB` i4% aXmS3ݽk{#Y׳&'Fk%<Ʋ 5} @*Ri\x4u~/vK{ G~ֵ왚Nzxؖ@6H9A )gzˏכ7y_8q|F+KX}P؈6 -2l*l3t?]|u}]O<|=\ӕgUőEvϽʅ ڪ&of %r65FaX!R0 IA 2A0jNoazmv4#dCLhE0pW8P(~q|rl6Y$L }hJ0:Va N@Xc!(XS2R!g٤FoFOs?\_o}a cxeqsǪmc^bF8*F1I]` 8q9\o1LC?So{? |" |_P:, !,P1:q % ! JN#%F*ׅLbip>$ L3pE*=fSwDZg F͕<X.vҰR,YX"c91@6nrBnom".&Is.9A-d_#`!m IL;N3|A kWJ 7LsOrK ytFJoj%DACb}& : :K:PyHb5G|:C.m"V"QF#UF&==, 2ۊ@=ߏ(8|ϳBmm[](B(2r1>(Q'YH{ {j=s|ypgT#47 +W.}7]ofg,2!@!x V "CȀhk|+ˌ02R02~59JGJ~ϿŒ+-:h? Ӓրb0K7@T3c{m[a qv, x^, !5X l>'/"kc88taf!wѝ98.^e'?N6WXWVA{n'Vcy`L94Kd#F#TFyDaJ2Q綻݋"rue 㕫e9 h˃g.uaHFaw#߮ͷPhj3RCGr5׻$EdƐz#Ė2 ~N~G\2 $G=(Z0 H,<(]J 8Wx}>U_ijcd$Bh5vtǎ}t.ft#Z*  "Do 2LbdF%{tcmc+(Kw*v~k!M,DZHU)qBb*K+,$sK1`%xgi2eit"DNDvOen2i.|gH\z݄Fc <)$,Ť6BeFH7#:d@4s]Kuʶ!4)XFln{ew?y]w>yKc4*^8DuO\8/Y쟝{C|#V\A;1An,CWAmKʐ!`ڰ4H%)QظdxlZ;!SUK_~Wΐ>-9Xwwz7+ ZH~ \+crOk} 9c<Í:ր$V+OG&h&JU~#}˝GZ7=( wZ8%&KW.wQʝwEh(ˌ &iwg?cD,ilRA^jѱ!Z /53֒c ,cZ7>+~{8r Mh׆ a-6W%KYY]&?︟Nyd@I|.i&wNÐ_"LZ8## 2ZMګ-ݘ0W[)toOG+PTuC~#HP摮G"Df'4 E1$ U]'?f&qP"m("ţZ^_[rck+ϳėXrQ?j+]<~'vf}u~JyoG/^hN aeR܀(ʰknyA8ppct77(n{1-ep>}s[M,m掻({y y n]S3gnw\_뙞W-!P$IF*.B(!MX$شI f\'rdjxM2L|Z f*<'="Ì9`seqL  /;gߒUDjƧL 8ΈLL-4XzHk+$ ՊYQQtR-~,pq8o#SmŌ$Jm-p\(I<y۲ޯW+?x}~SV@(@$R D# (1J$Z 2!"IZDOS-02R$vgsXz险 p=a ea1Z)5Z(Ff!X55űs\t'1{/A*e٤JsiW.Qtn9ky47Wp<ʮLNk_ v0@իOG{6'A (Ak Rm 4H%>2iWGZvųx%CXEζ-cX >s`~uyr^o}.ő"^ŲA)V!UV'KkcdD B)4hR e@(Z!s*StMƧ?%ldfHCksu]3s3x"/$WATCHDOG_LOG 2>&1 & /bin/sleep 2 PID=`/bin/ps auxwww \ | /bin/grep -v grep \ | /bin/grep $STAGE_MANAGER_WATCHDOG \ | /bin/awk '{print $2}'` if [ -z "$PID" ] then echo " The Stage_Manager_Watchdog did not start" exit 1 fi ;; start) # Schedule the real start for 5 minutes from now. echo "/etc/init.d/Stage_Manager start_now" | /usr/bin/at now + 5 minutes echo "Scheduled Stage_Manager start" ;; stop) echo "Shutting down the Stage_Manager_Watchdog" PID=`/bin/ps auxwww \ | /bin/grep -v grep \ | /bin/grep $STAGE_MANAGER_WATCHDOG \ | /bin/awk '{print $2}'` if [ -n "$PID" ] then /bin/kill -TERM $PID else echo " The Stage_Manager_Watchdog is not running" fi echo "Shutting down the Stage Manager" if [ -f $PID_FILE ] then Process_ID=`cat $PID_FILE` PID=`/bin/ps auxwww \ | /bin/grep -v grep \ | /bin/grep "$Process_ID" \ | /bin/awk '{print $2}'` if [ -n "$PID" ] then /bin/kill -TERM $PID else echo " The Stage_Manager is not running" fi /bin/rm -f $PID_FILE else echo " No $PID_FILE file" fi ;; restart) # Stop and then start the service. $0 stop $0 start_now ;; *) echo "Usage: $0 {start|start_now|restart|stop}" exit 1 ;; esac pirl-2.3.8/PIRL/Conductor/Maestro/Remote_Management_Exception.java0000644000175000017500000000344111742733133024742 0ustar mathieumathieu/* Remote_Management_Exception PIRL CVS ID: Remote_Management_Exception.java,v 1.6 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import java.lang.RuntimeException; /** A Remote_Management_Exception is thrown when a Remote_Theater Management interface method call is unable to fullfill the interface requirements.

@author Bradford Castalia - UA/PIRL @version 1.6 @see Remote_Theater */ public class Remote_Management_Exception extends RuntimeException { public static final String ID = "PIRL.Conductor.Maestro.Remote_Management_Exception (1.6 2012/04/16 06:04:11)"; public Remote_Management_Exception ( String message ) {super (message);} public Remote_Management_Exception ( String message, Throwable cause ) {super (message, cause);} public Remote_Management_Exception ( Throwable cause ) {super (cause);} } pirl-2.3.8/PIRL/Conductor/Maestro/Profile.java0000644000175000017500000015623711742733133020751 0ustar mathieumathieu/* Profile PIRL CVS ID: Profile.java,v 1.35 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Messenger.Message; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.Parser; import PIRL.PVL.PVL_Exception; import PIRL.Conductor.String_Vector_Comparator; import java.io.OutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Vector; import java.util.Date; /** A Profile contains Theater and Conductor definitions.

A Profile completely specifies the Conductor instances running, or to be run, on a set of Theaters. A Profile may take the form of a {@link Configuration} that can be {@link #Read(String) read} from or {@link #Write(String) written} to and file.

Theater definitions

A Theater definition is a list of one to three entries:

Theater location (String)
The location of a Theater in hostname[:port] format. The hostname specifies the system where the Stage_Manager with which to communicate is expected to be running. This name may be a full or short hostname or an IP address. The port specifies the network port to be used to communicate with the Stage_Manager. The port number and the colon delimiter may be ommitted in user supplied Profile Configuration files, in which case the current default Theater port number will be used.

The Theater location is a required entry in the definition. It must always be the first entry in the definition.

Conductor name (String)
The name of a Conductor definition in the Profile. At least one of the named Conductor definition instances are expected to be run at the Theater location.

The Conductor name is not required in the definition. It may be the second or third entry in the definition. A Theater definition witout a Conductor name is a "discovery" definition: A connection is to be made to the Theater and whatever Conductors are running there are to be discovered. However, a Theater definition must not have a Conductor name if it occurs within a Conductor definition, in which case the Conductor name is implicitly the name of the Conductor definition.

Count (Integer)
The number of named Conductor instances to be run at the Theater location.

The Count is not required in the definition, but must not be present if no Conductor name is present, unless the Theater definition occurs within a Conductor definition. It may be the second or third entry in the definition. The default Count is 1.

A Profile may contain zero or more Theater definitions. In a Profile Configuration the Theater definitions are always contained in an Assignment Parameter named "Theaters" with an Array Value that is a list of one or more Theater locations, or a list of one or more Theater definition Array Values. : a single multi-entry Theater definition must be contained in in an Array to distinguish it from a list of Theater locations; this results in doubled parentheses (or curly braces) around the single definition.

Each Theater definition in the Profile is guaranteed to have a unique combination of Theater location and Conductor name values. In a user supplied Profile Configuration file, however, a Theater location and Conductor name combination may occur in more than one Theater definition. The Count of each duplicate location/name combination is added to the Count of the initial definition. That is, Theater definitions read from Configuration files are accumulated into a consolidated list of unique definitions.

If a Conductor Theater definition with only a location entry is duplicated, the duplicates are ignored. However, a definition with the same location but that also contains a Conductor name causes the location-only definition to be ignored.

A Theaters parameter in a user supplied Profile Configuration file may occur at the top level of the file or within Conductor definitions. A Theater definition within a Conductor definition must not contain a Conductor name since this is implicitly the name of the containing Conductor definition.

Example Profile Configuration Theater definitions:

	# Single Theater location.
	Theaters = host
	Theaters = host:1234
	Theaters = (host)

	# Multiple Theater locations.
	Theaters = (host_1, host_2, host_3, host_3:1234)

	# Single Theater definition.
	Theaters = ((host, name, 1))
	Theaters = ((host, name))

	# Multiple Theater definitions.
	Theaters = ((host_1, name), (host_2, name, 2), (host_3, name_3, 3))
	Theaters =
		(
		host_1:1234,
		(host_2:1234, name),
		(host_3:1234, name_3, 3)
		)

Conductor definitions

A Conductor definition is an Aggregate (Group or Object) Parameter that specifies the command line arguments used to instantiate a Conductor at a Theater location. The name of the Aggregate is the Conductor name used in a Theater definition. All Parameter Aggregates in a Profile Configuration are taken to be Conductor definitions. The parameters contained in a Conductor definition are all Assignments with String values (except the Theaters parameter) and are all optional:

Pipeline
The name of the Pipeline to be managed by the Conductor.

If this parameter is not present it will be provided with the value being the Conductor definition name.

Configuration
The source of the Conductor Configuration file to be used by the Conductor. This may be a filesystem pathname or a URL. N.B.: A pathname is for the filesystem on the Theater host. A relative pathname is relative to the working directory of the Stage_Manager used to run the Conductor. In general, absolute pathnames may be preferrable to avoid the uncertainty of relative pathnames.

If this parameter is not present the instantiated Conductor will use its default Configuration source, which is expected to be the "Conductor.conf" pathname.

Server
The name of the Configuration Server that specifies the name of the Configuration parameters group containing database server access information to be used by the Conductor.

If this parameter is not present the instantiated Conductor will use the first entry in the Configuration Server parameter list.

Catalog
The name of the Database Catalog on the selected database server that contains the pipeline Sources and Procedures definitions.

If this parameter is not present the instantiated Conductor will use the Catalog prefix of the Pipeline (catalog.pipeline) if present; otherwise it will use the value of the "Catalog" name from the Configuration Server parameter group, or the default "Catalog" parameter in the Configuration.

Theaters
A Theater definition list. : The definitions in this list must not contain a Conductor name since this is implicitly the name of the Conductor definition.

A Profile may contain zero or more Conductor definitions. It is not necessary that all Conductor definitions be referenced by a Conductor name in a Theater definition. Any Conductor name in a Theater definition for which a Conductor definition has not been provided will be satisfied by a generic Conductor definition as if an emtpy group with the Conductor name had been provided.

All Conductor definitions will be unique; Conductor definitions with the same definition parameters must have different names. N.B.: A Profile Configuration file should contain only one Conductor definition Group (or Object) having a given name because when the Configuration is ingested Groups with the same name will have their parameter contents coalesced into a single Group; duplicate parameters names that are encountered will result in an exception being thrown.

Example Profile Configuration Conductor definitions:

	# A generic Conductor definition.
	Group = name
	End_Group

	# Simple Conductor definitions.
	Group = name
		Pipeline = pipeline
	End_Group

	Group = name_1
		Pipeline = pipeline
		Configuration = config_pathname_1
	End_Group

	Group = name_2
		Pipeline = pipeline_2
		Configuration = config_pathname_2
	End_Group

	# Complete Conductor definition.
	Group = name
		Pipeline = pipeline
		Configuration = /an/absolute/pathname
		Server = server_name
		Catalog = catalog_name
	End_Group

	# Conductor definition with Theater definitions.
	Group = pipeline
		Theaters = ((host_1), (host2, 2), (host_3, 3))
		Configuration = http://config.host/pathname/to/file
	End_Group

A Profile Configuration may be managed as separate files by taking advantage of the "@Include" capability of a Configuration. For example, sets of "standard" Conductor definitions may be maintained in files separate from various files that contain Theater definitions and "@Include" one or more of the standard Conductor definition sets:

	# Theater definitions.
	Theaters =
		(
		# Using standard Conductor definitions.
		(host_1, standard_1),
		(host_2, standard_2),

		# Using special processing Conductor definitions.
		(host_1, special_1),
		(host_2, special_2)

		# Custom Conductor definitions.
		(host_1, custom_1),

		# Local Conductor definition.
		(host_2, local, 3)
		)

	# Standard Conductor definitions.
	@Include = /Conductor/definitions/repository/Standard.defs

	# Special processing Conductor definitions.
	@Include = /Conductor/definitions/repository/Special_Processing.defs

	# Custom Conductor definitions.
	@Include = Custom_Conductor.defs

	# Local Conductor definition.
	Group = local
		Configuration = /an/absolute/pathname
	End_Group

In this example the included files may have many Conductor definitions, most of which are not used in any particular Theaters Theater definitions list.

"@Include" may also be used to provide common parts of various Coductor definitions by putting it inside the appropriate Conductor definitions.

Profiles are used by the {@link Kapellmeister} application.

@author Michael Wendell and Bradford Castalia, UA/HiROC @version 1.35 */ public class Profile { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Profile (1.35 2012/04/16 06:04:11)"; /** The name of a Profile {@link #Configuration() Configuration}. */ public static final String PROFILE_NAME = "Profile"; /** The Profile Configuration parameter name for a list of Theater definitions. */ public static final String THEATERS_PARAMETER_NAME = "Theaters"; /** The size of a complete Theater definition Vector. */ public static final int THEATER_DEFINITION_SIZE = 3; /** The index of the Theater location String in a theater definition Vector. */ public static final int THEATER_LOCATION_INDEX = 0; /** The index of the Conductor name String or Conductor_Definition in a theater definition Vector. */ public static final int CONDUCTOR_NAME_INDEX = 1; /** The index of the Conductor count Integer in a theater definition Vector. */ public static final int CONDUCTOR_COUNT_INDEX = 2; /** Theater Conductor name entry when no Conductor_Definition is provided. */ public static final String NO_CONDUCTOR_DEFINITION = null; /** The name of the Conductor definition parameter that specifies the pipeline to be processed. */ public static final String PIPELINE_PARAMETER_NAME = Conductor_Definition.PIPELINE_PARAMETER_NAME; private Vector> Theater_Definitions = new Vector> (); private Vector Conductor_Definitions = new Vector (); /** Undigested parameters after a Configuration source has been read and ingested. */ private Configuration Source_Parameters = null; private static final String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_READ = 1 << 2, DEBUG_ADD_CONDUCTOR_DEFINITION = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Profile from a source Configuration file.

Any parameters remaining after the source has been {@link #Read(String) read} will be held in the {@link #Unprocessed_Source_Parameters() unprocessed source Configuration}.

@param source A Configuration source String. This may be a file pathname or a URL. @throws Configuration_Exception If there was a problem with the source Configuration syntax. */ public Profile ( String source ) throws Configuration_Exception, PVL_Exception { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Profile: " + source); Read (source); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Profile"); } /** Construct a Profile from Theater and Conductor definitions.

@param theater_definitions A List of Theater definitions. @param conductor_definitions A List of Conductor_Definitions. @throws IllegalArgumentException If there was a problem creating the Profile. @see #Add(List, List) */ public Profile ( List> theater_definitions, List conductor_definitions ) throws IllegalArgumentException { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Profile:" + NL +" theater_definitions - " + theater_definitions + NL +" conductor_definitions -" + NL + conductor_definitions); Add (theater_definitions, conductor_definitions); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Profile"); } /** Construct an empty Profile. */ public Profile () { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Profile"); } /*============================================================================== Configuration */ /** Read Theater and Conductor definitions from a Configuration source file.

Theater and Conductor definitions found in the Configuration are added to the definitions currently in this Profile.

@param source A Configuration source String. This may be a file pathname or a URL. @return This Profile. @throws Configuration_Exception If the source can not be found or parsed as valid PVL or the contents do not conform to valid Profile syntax. @see #Read(Configuration) */ public Profile Read ( String source ) throws Configuration_Exception { if ((DEBUG & DEBUG_READ) != 0) System.out.println (">>> Profile.Read: " + source); Source_Parameters = new Configuration (); try { Source_Parameters.Defaults (false); Source_Parameters.Configure (source); } catch (Configuration_Exception exception) { throw new Configuration_Exception (ID + NL + "Could not read a Profile from the source -" + NL + source + NL + exception.getMessage ()); } return Read (Source_Parameters); } /** Read Theater and Conductor definitions from a Configuration.

Theater and Conductor definitions found in the Configuration are added to the definitions currently in this Profile. Only {@link #THEATERS_PARAMETER_NAME} Assignment parameters are used as Theater definitions. All Aggregate parameters are taken to be Conductor definitions.

N.B.: The Theater and Conductor definitions found in the Configuration are removed from the Configuration leaving only the {@link #Unprocessed_Source_Parameters() unprocessed source parameters} in the original Configuration.

@param configuration A Configuration. If null nothing is done. @return This Profile. @throws Configuration_Exception If the Configuration Theater or Conductor definition contents do not conform to valid Profile syntax. */ public Profile Read ( Configuration configuration ) throws Configuration_Exception { if ((DEBUG & DEBUG_READ) != 0) System.out.println (">>> Profile.Read: Configuration -" + NL + configuration); if ((Source_Parameters = configuration) == null) { if ((DEBUG & DEBUG_READ) != 0) System.out.println ("<<< Profile.Read: Configuration"); return this; } // Conductor Groups. if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Profile.Read: Finding Conductor groups"); Parameter parameter; while ((parameter = Source_Parameters.Find (Parameter.AGGREGATE)) != null) { if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Profile.Read: Conductor definition -" + NL + parameter.Description ()); Parameter invalid = parameter.Find (Parameter.AGGREGATE); if (invalid != null) throw new Configuration_Exception (ID + NL + "Invalid Profile syntax in the Configuration source -" + NL + configuration.Source () + NL + "The \"" + parameter.Name () + "\" Conductor definition " + "contains the \"" + invalid.Name () + "\" Aggregate parameter."); else { try {Add_Conductor_Definition (parameter);} catch (PVL_Exception exception) { throw new Configuration_Exception (exception.getMessage () + NL + "Problem reading the Configuration source -" + NL + configuration.Source ()); } } Source_Parameters.Remove (parameter); } // Theater definitions. if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Profile.Read: Finding Theaters"); if ((parameter = Source_Parameters.Find (Parameter.Absolute_Pathname (THEATERS_PARAMETER_NAME), Parameter.ASSIGNMENT)) != null) { // Theaters Value. if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Profile.Read: Theater definition -" + NL + parameter.Description ()); try {Add_Theater_Definitions (parameter.Value ());} catch (PVL_Exception exception) { throw new Configuration_Exception (exception.getMessage () + NL + "Problem reading the Configuration source -" + NL + configuration.Source ()); } Source_Parameters.Remove (parameter); } if (Source_Parameters.List_Size () == 0) Source_Parameters = null; if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Profile.Read: Remaining parameters - " + ((Source_Parameters == null) ? "null" : (NL + Source_Parameters.Description ())) + NL + NL +" Profile -" + NL + toString () + NL +"<<< Profile.Read"); return this; } /** Get unprocessed Configuration parameters.

@return A Configuration containing any parameters remaining after a Configuration source was {@link #Read(Configuration) read}. This will be null if there were no unprocessed parameters. */ public Configuration Unprocessed_Source_Parameters () {return Source_Parameters;} /** Write the Profile to a {@link #Configuration() Configuration} file.

If the Profile is empty nothing is written.

@param pathname The file pathname String. If null or empty nothing is done. @return This Profile. @throws Configuration_Exception If there was a problem creating a Configuration for this Profile. @throws IOException If there was a problem writing the file. @see #Write(OutputStream) */ public Profile Write ( String pathname ) throws Configuration_Exception, IOException { if (pathname == null || pathname.length () == 0) return this; OutputStream output_stream = null; try {output_stream = new FileOutputStream (pathname);} catch (IOException exception) { throw new IOException (ID + NL + "Could not write the Profile to -" + NL + new File (pathname).getAbsolutePath () + NL + exception.getMessage ()); } try {Write (output_stream);} catch (Configuration_Exception exception) { throw new Configuration_Exception (exception.getMessage () + NL + "File pathname: " + new File (pathname).getAbsolutePath ()); } catch (IOException exception) { throw new IOException (exception.getMessage () + NL + "File pathname: " + new File (pathname).getAbsolutePath ()); } return this; } /** Write the Profile to an OutputStream.

A {@link #Configuration() configuration} is created from the current Profile contents and this is {@link Configuration#Write(OutputStream) written} to the OutputStream.

If the Profile is empty nothing is written.

@param output_stream An OutputStream where the Profile Configuration is to be written. If null nothing is done. @return This Profile. @throws Configuration_Exception If there was a problem creating a Configuration for this Profile. @throws IOException If there was a problem writing the file. */ public Profile Write ( OutputStream output_stream ) throws Configuration_Exception, IOException { if (output_stream == null) return this; Configuration configuration = null; try {configuration = Configuration ();} catch (Configuration_Exception exception) { throw new Configuration_Exception (ID + NL + exception.getMessage () + NL + "Problem creating the Profile Configuration to write."); } if (configuration.List_Size () != 0) { try { configuration.Name (Parser.CONTAINER_NAME); ((Parameter)configuration.List ().get (0)) .Comments ("Profile of Theater and Conductor definitions." + NL + NL + Stage_Manager.DATE_FORMAT.format (new Date ()) + NL + "Generated by:" + NL + ID); configuration.Write (output_stream); } catch (PVL_Exception exception) { throw new Configuration_Exception (ID + NL + exception.getMessage () + NL + "Problem writing the Profile."); } catch (IOException exception) { throw new IOException (ID + NL + exception.getMessage () + NL + "Problem writing the Profile."); } } return this; } /** Get the Profile as a Configuration.

@return A Configuration containing the Profile definition parameters. The {@link Configuration#Name() Name} of the Configuration will be the {@link #PROFILE_NAME}. @throws Configuration_Exception If there was a problem creating the Configuration */ public Configuration Configuration () throws Configuration_Exception { int array_type = Value.Default_Array_Type (Value.SEQUENCE); Configuration configuration = new Configuration ((Parameter)null); if (Theater_Definitions.size () > 0) { // Get a Theater definitions list sorted on the location. Vector> definitions = Theater_Definitions (); Collections.sort (definitions, new String_Vector_Comparator (THEATER_LOCATION_INDEX)); try {configuration.Set (THEATERS_PARAMETER_NAME, definitions);} catch (Configuration_Exception exception) { Value.Default_Array_Type (array_type); throw new Configuration_Exception (ID + NL + exception.getMessage () + NL + "Could not create the Profile Configuration."); } } if (Conductor_Definitions.size () > 0) { int index = -1, size = Conductor_Definitions.size (); while (++index < size) { Conductor_Definition conductor_definition = Conductor_Definitions.get (index); if (conductor_definition != null) { try {configuration.Add (new Conductor_Definition (conductor_definition));} catch (PVL_Exception exception) { Value.Default_Array_Type (array_type); throw new Configuration_Exception (ID + NL + "Invalid Conductor definition for a Configuration -" + NL + conductor_definition + NL + exception.getMessage ()); } } } } Value.Default_Array_Type (array_type); configuration.Name (PROFILE_NAME); return configuration; } /** Get a PVL description of the Profile.

The current contents of the Profile are converted to a {@link #Configuration() Configuration} from which a PVL {@link Configuration#Description() description} is obtained.

@return A PVL String description of the Profile. This will be null if the {@link #Configuration() Configuration} description could not be generated. */ public String toString () { try {return Configuration ().Description ();} catch (Exception exception) {} return null; } /*============================================================================== Manipulators */ /** Adds Theater and Conductor definitions to the Profile.

@param theater_definitions A List of Theater definitions. @param conductor_definitions A List of Conductor_Definitions. @throws IllegalArgumentException If the arguments are not one List of valid Theater definitions and one List of valid Conductor_Definitions. */ public Profile Add ( List> theater_definitions, List conductor_definitions ) throws IllegalArgumentException { // Add the conductor definitions first for use by the theater definitions. Add_Conductor_Definitions (conductor_definitions); return Add_Theater_Definitions (theater_definitions); } /** Add a Conductor_Definition associated with a Theater Location.

The Theater definition will have as a Conductor name the {@link Conductor_Table_Model#Conductor_Name(Message) Conductor name} obtained from the Conductor definition, and it will have a Conductor count of 1.

@param theater_location A Theater location String. If null or empty nothing is done. @param conductor_definition A Conductor definition Message. If null nothing is done. @return This Profile. @throws IllegalArgumentException If the Conductor definition conflicts with an existing definition. @see #Add_Theater_Definition(String) @see #Add_Conductor_Definition(Message) */ public Profile Add ( String theater_location, Message conductor_definition ) throws IllegalArgumentException { if (theater_location == null || theater_location.length () == 0 || conductor_definition == null) return this; Add_Conductor_Definition (conductor_definition); Vector theater_definition = new Vector (THEATER_DEFINITION_SIZE); theater_definition.add (theater_location); theater_definition.add (Conductor_Table_Model.Conductor_Name (conductor_definition)); theater_definition.add (new Integer (1)); Add_a_Theater_Definition (theater_definition); return this; } /*------------------------------------------------------------------------------ Theaters */ /** Add Theater definitions from a Value.

@param theater_definitions A Value containing Theater definitions. */ private void Add_Theater_Definitions ( Value theater_definitions ) throws PVL_Exception { if (theater_definitions == null) return; if (! theater_definitions.Is_Array ()) // A single Value is an Array of one Value. theater_definitions.Type (Value.ARRAY); Vector definition; Value theater_definition, entry_one, entry_two; String name; int count = 0, index = -1, size = theater_definitions.Array_Size (); while (++index < size) { theater_definition = theater_definitions.Get (index); if (! theater_definition.Is_Array ()) // A single Value is an Array of one Value. theater_definition.Type (Value.ARRAY); switch (theater_definition.Array_Size ()) { case 0: // No entries. continue; case 1: // Location only. name = NO_CONDUCTOR_DEFINITION; count = 0; break; case 2: // Location and Name. entry_one = Check_Theater_Definition_Entry (theater_definition, CONDUCTOR_NAME_INDEX); if (entry_one.Is_Numeric ()) throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Invalid Theater definition to add -" + NL + theater_definition.Description () + NL + "The second of two entries must not be numeric."); name = entry_one.String_Data (); count = 1; break; case 3: // Location, Name, Count or Location, Count, Name. entry_one = Check_Theater_Definition_Entry (theater_definition, CONDUCTOR_NAME_INDEX); entry_two = Check_Theater_Definition_Entry (theater_definition, CONDUCTOR_COUNT_INDEX); if (entry_one.Is_Numeric () && entry_two.Is_Numeric ()) throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Invalid Theater definition to add -" + NL + theater_definition.Description () + NL + "Only one of the second and third entries may be numeric"); else if (! entry_one.Is_Numeric () && ! entry_two.Is_Numeric ()) throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Invalid Theater definition to add -" + NL + theater_definition.Description () + NL + "Either the second or third entry must be numeric."); else if (entry_one.Is_Numeric () && ! entry_two.Is_Numeric ()) { name = entry_two.String_Data (); count = (int)entry_one.long_Data (); } else { name = entry_one.String_Data (); count = (int)entry_two.long_Data (); } break; default: throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Invalid Theater definition to add -" + NL + theater_definition.Description () + NL + "There are more than the maximum of " + THEATER_DEFINITION_SIZE + " entries."); } definition = new Vector (THEATER_DEFINITION_SIZE); entry_one = Check_Theater_Definition_Entry (theater_definition, THEATER_LOCATION_INDEX); definition.add (entry_one.String_Data ()); if (name == null || name.length () == 0 || count <= 0) { name = NO_CONDUCTOR_DEFINITION; count = 0; } definition.add (name); definition.add (count); Add_a_Theater_Definition (definition); } } private static Value Check_Theater_Definition_Entry ( Value theater_definition, int entry ) throws PVL_Exception { Value value = theater_definition.Get (entry); if (value.Is_Array ()) throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Invalid Theater definition to add -" + NL + theater_definition.Description () + NL + "Entry " + entry + " must not be an Array."); return value; } /** Add a list of Theater definitions to the Profile.

@param theater_definitions A List containing Theater definitions. If null or empty nothing is done. @return This Profile. @throws IllegalArgumentException If any Theater definition in the theater_definitions is not {@link #Validate_Theater_Definition(List) valid}. @see #Add_Theater_Definition(List) */ public Profile Add_Theater_Definitions ( List> theater_definitions ) throws IllegalArgumentException { if (theater_definitions == null || theater_definitions.size () == 0) return this;; try {Validate_Theater_Definitions (theater_definitions);} catch (IllegalArgumentException exception) { throw new IllegalArgumentException (exception.getMessage () + NL + "The Theater definitions list was not added."); } int index = -1, size = theater_definitions.size (); while (++index < size) Add_a_Theater_Definition (new Vector (theater_definitions.get (index))); return this; } /** Add a Theater definition to the Profile.

The theater definition is first {@link #Validate_Theater_Definition(List) validated}. It is then copied and added to the Profile.

@param theater_definition A List containing a Theater definition. If null or empty nothing is done. @return This Profile. @throws IllegalArgumentException If the theater_definition is not valid. */ public Profile Add_Theater_Definition ( List theater_definition ) throws IllegalArgumentException { if (theater_definition == null || theater_definition.size () == 0) return this; try {Validate_Theater_Definition (theater_definition);} catch (IllegalArgumentException exception) { throw new IllegalArgumentException (exception.getMessage () + NL + "The Theater definition was not added."); } Add_a_Theater_Definition (new Vector (theater_definition)); return this; } /** Add a general Theater definition.

A general Theater definition has a Theater location but no Conductor name (this will be null) or count (this will be zero).

Adding a general Theater definition for a location that already has a Theater definition in the Profile will have no effect; a general definition does not override a specific definition.

@param theater_location A Theater location String. If this is null or empty nothing will be done. @return This Profile. */ public Profile Add_Theater_Definition ( String theater_location ) { if (theater_location != null && theater_location.length () != 0) { Vector definition = new Vector (THEATER_DEFINITION_SIZE); definition.add (theater_location); definition.add (null); definition.add (new Integer (0)); Add_a_Theater_Definition (definition); } return this; } private void Add_a_Theater_Definition ( // The definition must be valid. Vector definition ) { String conductor_name = (String)definition.get (CONDUCTOR_NAME_INDEX); // Set the location to the standard format. String theater_location = Theater.Full_Location ((String)definition.get (THEATER_LOCATION_INDEX)); definition.set (THEATER_LOCATION_INDEX, theater_location); // Check for a existing Theater definition that matches. Vector theater_definition = Theater_Definition (theater_location, conductor_name); if (theater_definition != null && conductor_name != null) { // Matching non-null Conductor names; increment the Conductor count. Integer count = (Integer)theater_definition.get (CONDUCTOR_COUNT_INDEX); theater_definition.set (CONDUCTOR_COUNT_INDEX, count += (Integer)definition.get (CONDUCTOR_COUNT_INDEX)); } else if (theater_definition == null) { // Non-matching. if (! Contains_Theater_Definition (theater_location)) // New definition. Theater_Definitions.add (definition); else if (conductor_name != null) { if ((theater_definition = Theater_Definition (theater_location, null)) != null) { // Make the general definition specific. theater_definition.set (CONDUCTOR_NAME_INDEX, conductor_name); theater_definition.set (CONDUCTOR_COUNT_INDEX, definition.get (CONDUCTOR_COUNT_INDEX)); } else // New definition. Theater_Definitions.add (definition); } // A specific definition will not be made general. } // Matching null Conductor names ignored. } /** Validate a Theater definition.

A valid Theater definition Vector contains three entries:

Theater Location
The Theater location String is in the form:

host[:port]

The host may be a hostname, short or fully qualified, or IP address. The optional port is the system port number used to connect to the Stage_Manager.

Conductor Name
The Conductor name String is the name of a {@link #Conductor_Definition(String) Conductor_Definition} in the Profile. This may be null to indicate that the Conductors on the Theater are to be used as-is ("discovered").
Conductor Count
The Conductor count Integer specifies the number of Conductors as defined by the Conductor_Defintiion having the Conductor name that are to be run on the Theater.
The Theater location must always be the first entry in the definition. The Conductor name and count need not be in that order. N.B.: If the Conductor name and count are not in that order their order in the Vector will be swapped.

@param theater_definition A List containing a Theater definition. @throws IllegalArgumentException If the theater_definition is invalid. */ public static void Validate_Theater_Definition ( List theater_definition ) throws IllegalArgumentException { if (theater_definition == null || theater_definition.size () != THEATER_DEFINITION_SIZE) throw new IllegalArgumentException (ID + NL + "Invalid Theater definition -" + NL + theater_definition + NL + "A Theater definition must contain three entries."); if (! (theater_definition.get (THEATER_LOCATION_INDEX) instanceof String)) throw new IllegalArgumentException (ID + NL + "Invalid Theater definition -" + NL + theater_definition + NL + "The first entry of a Theater definition must be a String."); Object entry_one = theater_definition.get (CONDUCTOR_NAME_INDEX), entry_two = theater_definition.get (CONDUCTOR_COUNT_INDEX); if ((entry_one instanceof String || entry_one == null ) && entry_two instanceof Integer) return; if (entry_one instanceof Integer && (entry_two instanceof String || entry_two == null)) { // Swap name and count entries. theater_definition.set (CONDUCTOR_NAME_INDEX, entry_two); theater_definition.set (CONDUCTOR_COUNT_INDEX, entry_one); return; } throw new IllegalArgumentException (ID + NL + "Invalid Theater definition -" + NL + theater_definition + NL + "The second and third entries of a Theater definition" + NL + "must be a String and Integer (in either order)."); } /** Validate a Theater definitions list.

@param theater_definitions The List that is to be validated. If null or empty nothing is validated. @throws IllegalArgumentException If a theater_definitions entry is invalid. @see #Validate_Theater_Definition(List) */ public static void Validate_Theater_Definitions ( List> theater_definitions ) throws IllegalArgumentException { if (theater_definitions == null || theater_definitions.isEmpty ()) return; int index = -1, size = theater_definitions.size (); try {while (++index < size) Validate_Theater_Definition (theater_definitions.get (index));} catch (IllegalArgumentException exception) { throw new IllegalArgumentException (exception.getMessage () + NL + "at entry " + index + " of a theaters definitions list."); } } /** Get a Theater definition at its Profile index.

@param index A Profile Theater definition index. @return A reference to the Theater definition Vector in this Profile; the definition is not copied. This will be null if the index is invalid. */ public Vector Theater_Definition ( int index ) { if (index < 0 || index >= Theater_Definitions.size ()) return null; return Theater_Definitions.get (index); } /** Get the Profile index of the next Theater definition for a Theater location starting after a given index.

Starting at the Profile index immediately following the specified index, the index of the next Theater definition for the specified location is returned.

@param theater_location A Theater location String. @param index The Profile index after which the search for the next Theater definition for the the location will begin. If less than zero the search begins with the first definition. @return The Profile index of the next Theater definition that contains a matching location, or -1 if no match is found or the theater location is null or the empty String. @see #Theater_Definition(int) */ public int Next_Theater_Index ( String theater_location, int index ) { if (theater_location != null && theater_location.length () != 0) { theater_location = Theater.Full_Location (theater_location); if (index < -1) index = -1; int size = Theater_Definitions.size (); while (++index < size) if (theater_location.equals (Theater_Definitions.get (index) .get (THEATER_LOCATION_INDEX))) return index; } return -1; } /** Get the list of Theater definitions.

@return A Vector containing a copy of the Theater definitions list in this Profile. */ public Vector> Theater_Definitions () { int index = -1, size = Theater_Definitions.size (); Vector> theater_definitions = new Vector> (size); Vector this_definition, that_definition; /* Note that all the theater definition objects are final so the object references can simply be copied from this current definition to the that new definition without having to construct clones of the objects. */ while (++index < size) { this_definition = Theater_Definitions.get (index); that_definition = new Vector (THEATER_DEFINITION_SIZE); that_definition.add (this_definition.get (THEATER_LOCATION_INDEX)); that_definition.add (this_definition.get (CONDUCTOR_NAME_INDEX)); that_definition.add (this_definition.get (CONDUCTOR_COUNT_INDEX)); theater_definitions.add (that_definition); } return theater_definitions; } /** Get the Theater definition for a Theater location and Conductor name.

@param theater_location A Theater location String. If null, null is returned. @param conductor_name A Conductor name. If null, null is returned. @return A Theater definition Vector. N.B.: This is a reference to the definition in the Profile; copy first if it is to be modified. This will be null if the matching Theater definition could not be found. */ public Vector Theater_Definition ( String theater_location, String conductor_name ) { int index = Theater_Definition_Index (Theater_Definitions, theater_location, conductor_name); if (index >= 0) return Theater_Definitions.get (index); return null; } /** Get the canonical Theater definitions list.

N.B.: The {@link #CONDUCTOR_NAME_INDEX} entry in the definition Vector will be the full Conductor_Definition, not just its name. This will be null if the definition for the Theater does not specify any Conductors.

@return A Vector containing of all the Theater definitions in the Profile. N.B.: The Conductor_Definitions in the Theater definition Vectors will be references to the objects in the Profile, not copies. */ public Vector> Canonical_Theater_Definitions () { Vector> theater_definitions = new Vector> (); int index = -1, size = Theater_Definitions.size (); while (++index < size) { Vector theater_definition = Theater_Definitions.get (index); Vector definition = new Vector (); // Theater location. definition.add (theater_definition.get (THEATER_LOCATION_INDEX)); // Conductor definition. String conductor_name = (String)theater_definition.get (CONDUCTOR_NAME_INDEX); if (conductor_name == null || conductor_name.equals (NO_CONDUCTOR_DEFINITION)) definition.add (null); else { Conductor_Definition conductor_definition = Conductor_Definition (conductor_name); if (conductor_definition != null) definition.add (conductor_definition); else definition.add (Generic_Conductor_Definition (conductor_name)); } // Conductor count. definition.add (theater_definition.get (CONDUCTOR_COUNT_INDEX)); theater_definitions.add (definition); } return theater_definitions; } /** Test if any Theater definition for a location is present in this Profile.

@param theater_location A Theater location String. If null or empty false is returned. @return true if at least one Theater defifinition for the theater_location is present in this Profile; false otherwise. */ public boolean Contains_Theater_Definition ( String theater_location ) { if (theater_location != null && theater_location.length () != 0) { int index = Theater_Definitions.size (); while (--index >= 0) if (theater_location.equals (Theater_Definitions.get (index) .get (THEATER_LOCATION_INDEX))) return true; } return false; } /** Get the index of a Theater definition for a Theater location and Conductor name in a Vector of Theater definitions.

@param theater_definitions A Vector of Theater definitions. If null or empty -1 is returned. N.B.: This must be a {@link #Validate_Theater_Definitions(List) valid Theater definitions list}. @param theater_location A Theater location String. If null or empty -1 is returned. @param conductor_name A Conductor name String. May be null. @return The index of the Theater definition in the theater_definitions. This will be -1 if a Theater definition with the theater_location and conductor_name could not be found. */ public static int Theater_Definition_Index ( List theater_definitions, String theater_location, String conductor_name ) { if (theater_definitions != null && theater_definitions.size () != 0 && theater_location != null && theater_location.length () != 0) { theater_location = Theater.Full_Location (theater_location); int index = theater_definitions.size (); while (--index >= 0) { List theater_definition = (List)theater_definitions.get (index); if (theater_location.equals ((String)theater_definition.get (THEATER_LOCATION_INDEX))) { String name = (String)theater_definition.get (CONDUCTOR_NAME_INDEX); if ((conductor_name != null && conductor_name.equals (name)) || (conductor_name == null && name == null)) return index; } } } return -1; } /** Get the number of Theater definitions in the Profile.

@return The number of Theater definitions in the Profile. */ public int Total_Theater_Definitions () {return Theater_Definitions.size ();} /** Remove a Theater definition from the Profile for a Theater location and Conductor name.

@param theater_location A Theater location String. If null or empty null will be returned. @param conductor_name A Conductor name String. May be null. @return The Theater definition Vector that was removed from the Profile. This will be null if a matching definition could not be found. */ public Vector Remove_Theater_Definition ( String theater_location, String conductor_name ) {return Remove_Theater_Definition (Theater_Definition_Index (Theater_Definitions, theater_location, conductor_name));} /** Removes a Theater definition at a {@link #Theater_Definitions()} index.

@param index The index of a Theater definition in this Profile. @return The Theater definition Vector that was removed from the Profile. This will be null if index is not valid. */ public Vector Remove_Theater_Definition ( int index ) { if (index >= 0 && index < Theater_Definitions.size ()) return Theater_Definitions.remove (index); return null; } /** Clear the Theater definitions list.

@return This Profile. */ public Profile Clear_Theater_Definitions () {Theater_Definitions.clear (); return this;} /*------------------------------------------------------------------------------ Conductors */ /** Add Conductor_Definitions to the Profile.

@param conductor_definitions A List of Conductor_Definitions. @return This Profile. @throws IllegalArgumentException If a definition conflicts with an existing definition of the same name. @see #Add_Conductor_Definition(Message) */ public Profile Add_Conductor_Definitions ( List conductor_definitions ) throws IllegalArgumentException { if (conductor_definitions == null || conductor_definitions.size () == 0) return this; int index = -1, size = conductor_definitions.size (); while (++index < size) Add_Conductor_Definition (conductor_definitions.get (index)); return this; } /** Adds a Conductor definition from a Message.

@param message A Message containing a Conductor definition to be added to this Profile. @return This Profile. @throws IllegalArgumentException If the Message does not contain a valid Conductor definition or contains a conflicting definition with a definition of the same name already in the Profile. */ public Profile Add_Conductor_Definition ( Message message ) throws IllegalArgumentException { if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (">>> Add_Conductor_Definition (Message):" + NL + message); if (message == null || message.List_Size () == 0) { if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println ("<<< Add_Conductor_Definition (Message)"); return this; } try { Conductor_Definition definition = new Conductor_Definition (message), current_definition = Conductor_Definition (definition.Name ()); if (current_definition == null) { Conductor_Definitions.add (definition); if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (" New definition added"); } else if (! current_definition.Matches (definition)) throw new IllegalArgumentException (ID + NL + "The Conductor definition to be added -" + NL + definition + NL + "- has the same name but different definition values" + NL + "as a Conductor definition already in the Profile -" + NL + current_definition); } catch (PVL_Exception exception) { throw new IllegalArgumentException (ID + NL + "The Conductor definition to be added could not be copied -" + NL + exception.getMessage () + NL + message); } if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (" Conductor_Definitions -" + NL + Conductor_Definitions + NL +"<<< Add_Conductor_Definition (Message)"); return this; } /** Add a Conductor definition assembled from a Parameter.

@param conductor_definition An Aggregate Parameter containing the {@link Conductor_Definition} parameters. */ private void Add_Conductor_Definition ( // Guaranteed to be an Aggregate Parameter conductor_definition ) throws PVL_Exception { if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (">>> Add_Conductor_Definition (Parameter):" + NL + conductor_definition.Description ()); // Scratch copy Message definition = new Message (conductor_definition); if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (" Scratch Message -" + NL + definition); // Name: if (conductor_definition.Name ().length () == 0) throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "The Conductor definition to be added has an empty name -" + NL + conductor_definition.Description ()); definition.Name (conductor_definition.Name ()); // Pipeline: if (definition.Get (PIPELINE_PARAMETER_NAME) == null) { definition.Set (PIPELINE_PARAMETER_NAME, definition.Name (), 0); if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (" " + PIPELINE_PARAMETER_NAME + " set to " + definition.Name () + " from Name"); } // Theaters. // The Parameter is from a Configuration so there can be only one Theaters. Value theaters = definition.Value_of (THEATERS_PARAMETER_NAME); if (theaters != null) { if (! theaters.Is_Array ()) // Single Value Array theaters.Type (Value.ARRAY); if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println (" " + THEATERS_PARAMETER_NAME + " = " + theaters.Description ()); int index = theaters.Array_Size (); while (--index >= 0) { Value theater = (Value)theaters.Get (index); if (! theater.Is_Array ()) // Single Value Array theater.Type (Value.ARRAY); if (theater.Array_Size () == 0) { theaters.Remove (theater); continue; } theater.Add (new Value (definition.Name ())); } // Remove the Theaters from the Conductor_Definition. definition.Remove (THEATERS_PARAMETER_NAME); Add_Theater_Definitions (theaters); } Add_Conductor_Definition (definition); if ((DEBUG & DEBUG_ADD_CONDUCTOR_DEFINITION) != 0) System.out.println ("<<< Add_Conductor_Definition (Parameter)"); } /** Test if this Profile contains a Conductor_Definition matching a Message.

Each Conductor_Definition contained in this Profile is compared against the contents of the Message for a {@link Conductor_Definition#Matches(Message) matching definition}. N.B.: The name of the Message does not need to match the name of a Conductor_Definition, only the contents must logically match.

@param message A Message. If null false is returned. @return true if this Profile contains a Conductor_Definition that logically matches the Message contents; false if no match is found. */ public boolean Contains_Conductor_Definition ( Message message ) { if (message != null) { int index = Conductor_Definitions.size (); while (--index >= 0) if (Conductor_Definitions.get (index).Matches (message)) return true; } return false; } /** Get the list of Conductor_Definitions.

@return A Vector of Conductor_Definitions. This is a copy of the Conductor definitions list contained in this Profile. */ public Vector Conductor_Definitions () { int index = -1, size = Conductor_Definitions.size (); Vector conductor_definitions = new Vector (size); while (++index < size) { try {conductor_definitions.add (new Conductor_Definition (Conductor_Definitions.get (index)));} catch (PVL_Exception exception) {/* Shouldn't happen. Valid Conductor_Definition.*/} } Vector already_added = new Vector (); String name; index = -1; size = Theater_Definitions.size (); while (++index < size) { name = (String)Theater_Definitions.get (index).get (CONDUCTOR_NAME_INDEX); if (name != null && ! name.equals (NO_CONDUCTOR_DEFINITION) && Conductor_Definition_Index (Conductor_Definitions, name) < 0 && ! already_added.contains (name)) { already_added.add (name); conductor_definitions.add (Generic_Conductor_Definition (name)); } } return conductor_definitions; } /** Get a Conductor_Definition index from a list of Conductor_Definition objects using its name.

@param conductor_definitions A List of Conductor_Definition objects. @param conductor_name The Conductor_Definition name. @return The index of the definition in the Conductor_Definitions Vector. */ public static int Conductor_Definition_Index ( List conductor_definitions, String conductor_name ) { if (conductor_definitions != null && conductor_definitions.size () != 0 && conductor_name != null && conductor_name.length () != 0) { int index = conductor_definitions.size (); while (--index >= 0) if (conductor_name.equals (conductor_definitions.get (index).Name ())) return index; } return -1; } /** Get the Conductor_Definition for a Conductor name.

@param conductor_name A Conductor_Definition name String. If null, null is returned. @return The Conductor_Definition in the Profile (not a copy) having the conductor_name. This will be null if a Conductor_Definition with the conductor_name could not be found. */ public Conductor_Definition Conductor_Definition ( String conductor_name ) { int index = Conductor_Definition_Index (Conductor_Definitions, conductor_name); if (index >= 0) return Conductor_Definitions.get (index); return null; } /** Get the number of Conductor_Definitions in the Profile.

@return The number of Conductor_Definitions in the Profile. */ public int Total_Conductor_Definitions () {return Conductor_Definitions.size ();} /** Remove a Conductor_Definition by name.

@param conductor_name A Conductor name. @return The Conductor_Definition that was removed. This will be null if a Conductor_Definition with the specified name could not be found. */ public Conductor_Definition Remove_Conductor_Definition ( String conductor_name ) { int index = Conductor_Definition_Index (Conductor_Definitions, conductor_name); if (index >= 0) return Conductor_Definitions.remove (index); return null; } /** Removes a definition from the Conductor_Definitions Vector based on the index.

*/ public void Remove_Conductor_Definition ( int index ) { if (Conductor_Definitions.size () >= index) Conductor_Definitions.remove (index); } /** Clears the Conductor_Definitions Vector.

@return This Profile. */ public Profile Clear_Conductor_Definitions () {Conductor_Definitions.clear (); return this;} /** Returns a generic Conductor_Definition for a given name.

@param conductor_name The name for the generic Conductor_Definition. @return A Conductor_Definition. */ private Conductor_Definition Generic_Conductor_Definition ( String conductor_name ) { if (conductor_name != null && conductor_name.length () != 0) return new Conductor_Definition (conductor_name); return null; } } pirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Table_Model_Event.java0000644000175000017500000000631611742733133024671 0ustar mathieumathieu/* Conductor_Table_Model_Event PIRL CVS ID: Conductor_Table_Model_Event.java,v 1.6 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import javax.swing.table.TableModel; import javax.swing.event.TableModelEvent; /** A Conductor_Table_Model_Event extends a TableModelEvent with Conductor processing status information.

@author Bradford Castalia, UA/PIRL @version 1.6 */ public class Conductor_Table_Model_Event extends TableModelEvent { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Table_Model_Event (1.6 2012/04/16 06:04:11)"; /** The processing state of the Conductor with which the event is associated. */ public int Processing_State = 0; /*============================================================================== Constructors: TableModelEvent */ public Conductor_Table_Model_Event ( TableModel source ) {super (source);} public Conductor_Table_Model_Event ( TableModel source, int row ) {super (source, row);} public Conductor_Table_Model_Event ( TableModel source, int first_row, int last_row ) { super (source, first_row, last_row); Confirm_Row_Order (); } public Conductor_Table_Model_Event ( TableModel source, int first_row, int last_row, int column ) { super (source, first_row, last_row, column); Confirm_Row_Order (); } /** Construct a Conductor_Table_Model_Event. */ public Conductor_Table_Model_Event ( TableModel source, int first_row, int last_row, int column, int type ) { super (source, first_row, last_row, column, type); Confirm_Row_Order (); } private void Confirm_Row_Order () { if (firstRow > lastRow) { int row = firstRow; firstRow = lastRow; lastRow = row; } } /*============================================================================== Accessors */ /** Set the Conductor processing state of the event.

@param processing_state The processing state value. @return This Conductor_Table_Model_Event. */ public Conductor_Table_Model_Event Processing_State ( int processing_state ) {Processing_State = processing_state; return this;} /** Get the Conductor processing state of the event.

@return The processing state value. */ public int Processing_State () {return Processing_State;} } pirl-2.3.8/PIRL/Conductor/Maestro/Kapellmeister0000755000175000017500000000546111652360433021224 0ustar mathieumathieu#!/usr/bin/perl # # Kapellmeister # # A wrapper for the Kapellmeister Conductor management client. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath: # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # SwingX - http://swinglabs.org/ # # The pathname to the SwingLabs Swing Component Extensions jar file or # the root directory where its class files are located. This package is # a required dependency. # Default: $PIRL_JAVA_HOME/SwingX/swingx.jar # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # Use the KAPELLMEISTER_MEMORY environment variable to control the # amount of memory allocated to the java virutal machine. When working # with a large number of Stage_Managers and Conductors sufficient # memory must be allocated beyond the usual JVM size. The default, if # the KAPELLMEISTER_MEMORY environment variable is not used, is 256m. # # CVS ID: Kapellmeister,v 2.2 2011/10/27 22:54:19 castalia Exp $KAPELLMEISTER_MEMORY = "256m" unless $KAPELLMEISTER_MEMORY = $ENV{"KAPELLMEISTER_MEMORY"}; $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); $SwingX = "$PIRL_JAVA_HOME/SwingX/swingx.jar" unless $SwingX = $ENV{"SwingX"}; Add_to_CLASSPATH ($SwingX); @Arguments = ("java", "-Xmx$KAPELLMEISTER_MEMORY"); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Conductor.Maestro.Kapellmeister", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/Maestro/Manage_Remote0000755000175000017500000000765211215576303021132 0ustar mathieumathieu#!/bin/tcsh # Manage Stage_Managers on remote systems. # # Author: Bradford Castalia, UA/HiROC # # CVS ID: Manage_Remote,v 1.5 2009/06/16 02:00:35 castalia Exp set User = $USER if ($?STAGE_MANAGER_LOG_DIR) then set Working_Dir = $STAGE_MANAGER_LOG_DIR else set Working_Dir = "/HiRISE/Users/$USER/Stage_Manager" endif set Active_Nodes = (`list_active_nodes`) while ($#argv) switch ($1) case [Ss][Tt][Aa]*: set op = "start" goto Set_Operation case [Rr][Ee]*: set op = "restart" goto Set_Operation case [Ss][Tt][Oo]*: set op = "stop" goto Set_Operation case [Cc][Ll]*: set op = "clear" goto Set_Operation case [Cc][Hh]*: set op = "check" Set_Operation: if ($?Operation) then if ($Operation != $op) then echo "Multiple operations - $Operation and $op" echo goto Usage endif endif set Operation = $op breaksw case -[Dd]*: shift if (! $#argv) then echo 'Missing directory pathname.' echo goto Usage endif set Working_Dir = $1 breaksw case -[Ss]*: shift if (! $#argv) then echo 'Missing system name(s).' echo goto Usage endif set Sys_List = ($1) breaksw case -[Rr]*: set User = "root" breaksw case -[Vv]*: set Verbose = 1 breaksw case help: case -[Hh]*: Usage: echo 'Usage:' $0:t '[] [-Working_Directory ] [-root]' echo echo 'Manage remote System_Managers.' echo echo 'Operation is one of -' echo ' start' echo ' stop' echo ' restart' echo ' Runs the Manage_Stage_Manager on the target systems.' echo ' clear' echo ' Removes the root Stage_Manager log files on the target systems.' echo ' check' echo ' Checks for running Stage_Managers on the target systems.' echo 'The default operation is check.' echo echo '-Directory ' echo ' The pathname is set as the current working directory' echo ' and the Manage_Stage_Manager STAGE_MANAGER_LOG_DIR.' echo ' This option is ignored when -Root is specified.' echo ' The default may be set by $STAGE_MANAGER_LOG_DIR.' echo " Default: $Working_Dir" echo echo '-System " [...]"' echo ' A list of one or more system hostnames where the Stage_Manager operation will be done.' echo ' Multiple names must be space separated and the entire list enclosed in quotes.' echo " Default (from list_active_nodes): $Active_Nodes" echo echo '-Root' echo ' The /etc/init.d/Stage_Manager procedure is used to start root Stage_Managers.' echo " Default: /opt/local/sh/Manage_Stage_Manager is used to start $USER Stage_Managers." echo echo '-Verbose' echo ' List each remote command that is excecuted.' echo ' Default: List only each remote system used.' echo echo 'See also Manage_Stage_Manager -help' echo exit 1 breaksw default: echo "Unknown command line argument: $1" echo goto Usage endsw shift end if (! $?Sys_List) then set Sys_List = ($Active_Nodes) endif if (! $?Operation) set Operation = check if ($Operation == "check") then set Command = "ps auxwww | grep $User | grep PIRL.Conductor.Maestro.Stage_Manager | grep -v grep" else if ($Operation == "clear") then if ($User != "root") then echo 'The clear operation can only be used by root.' echo 'The restart operation will automatically clear private logs.' exit 1 endif set Command = "rm -f /var/log/Stage_Manager/Stage_Manager.log /var/log/Stage_Manager/Stage_Manager_Watchdog.log" else if ($User == "root") then if ($Operation == "start") then set Operation = "start_now" endif set Command = "/etc/init.d/Stage_Manager $Operation" else set Command = "cd $Working_Dir; setenv STAGE_MANAGER_LOG_DIR $Working_Dir; /opt/local/sh/Manage_Stage_Manager $Operation" endif endif foreach node ($Sys_List) if ($?Verbose) then echo ssh $User@$node $Command else echo "$node -" endif ssh $User@$node "$Command" echo end exit 0 pirl-2.3.8/PIRL/Conductor/Maestro/Makefile0000644000175000017500000000231311061407027020121 0ustar mathieumathieu# Makefile for Java Conductor.Maestro package. # # PIRL CVS ID: Makefile,v 1.19 2008/09/09 05:48:07 castalia Exp # gmake syntax # Installation location for the executable scripts. SCRIPTS_DIR ?= /opt/local/sh # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm # Location of SwingX Classes. SwingX ?= /opt/java/SwingX/swingx.jar JPATH ?= ../../..:$(JCM):$(SwingX) JC ?= javac CLASSES := Remote_Management_Exception.class \ Theater_Protocol_Exception.class \ Theater.class \ Local_Theater.class \ Remote_Theater.class \ New_Conductor_Dialog.class \ Conductor_Table_Model_Event.class \ Conductor_Table_Model.class \ Conductor_Table.class \ Conductor_Matrix_Model.class \ Conductor_Matrix.class \ Conductor_Definition.class \ Profile.class \ Theater_List_Model.class \ Theater_List.class \ Kapellmeister.class \ Stage_Manager.class \ Error_Report.class SCRIPTS := Kapellmeister \ Stage_Manager # Targets: all: classes classes: $(CLASSES) install: classes cp -p $(SCRIPTS) $(SCRIPTS_DIR) clean: rm -f *.class .SUFFIXES: .java .class .java.class: $(JC) -classpath $(JPATH) $(JFLAGS) $< pirl-2.3.8/PIRL/Conductor/Maestro/Kapellmeister.conf0000644000175000017500000000273011121337730022134 0ustar mathieumathieu/* Kapellmeister Configuration CVS ID: Kapellmeister.conf,v 1.7 2008/12/15 02:24:24 castalia Exp */ Group = Kapellmeister /* Display tooltips. */ Tooltips = true /* Confirm abort on quit of a running Conductor. */ Confirm_Conductor_Abort = true /* Conductors started from a Profile should wait-to-start. */ Profile_Wait_to_Start = true /* Password to authorize connection to the Stage_Manager. If no password is provided and it is required by the Stage_Manager one will requested in a dialog when the Kapellmeister starts. */ Password = "" /* Communications port number. */ Port = 4144 /* Connection message receive timeout (seconds). The minimum value is 5 seconds. */ Receive_Timeout = 10 End_Group /* Conductor Manager parameters. */ Group = Conductor /* Display the splash screen during Manager startup. This may be a time in seconds or a true/false (enabled/disabled) value. */ Splash_Screen = true /* Manager window size and display location. */ Manager_Width = 750 Manager_Height = 463 Manager_Location_X = 300 Manager_Location_Y = 100 /* Manager Log Monitor window size. */ Monitor_Width = 700 Monitor_Height = 400 /* Display tooltips on Manager GUI components. */ Tooltips = true End_Group pirl-2.3.8/PIRL/Conductor/Maestro/Stage_Manager0000755000175000017500000000470511652360406021120 0ustar mathieumathieu#!/usr/bin/perl # # Stage_Manager # # A wrapper for the Stage_Manager Conductor proxy server. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath: # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # Use the STAGE_MANAGER_MEMORY environment variable to control the # amount of memory allocated to the java virutal machine. When working # with a large number of Kapellmeisters and Conductors sufficient # memory must be allocated beyond the usual JVM size. The default, if # the KAPELLMEISTER_MEMORY environment variable is not used, is 128m. # # CVS ID: Stage_Manager,v 2.2 2011/10/27 22:53:58 castalia Exp $STAGE_MANAGER_MEMORY = "128m" unless $STAGE_MANAGER_MEMORY = $ENV{"STAGE_MANAGER_MEMORY"}; $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); @Arguments = ("java", "-Xmx$STAGE_MANAGER_MEMORY"); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Conductor.Maestro.Stage_Manager", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/Maestro/Kapellmeister.java0000644000175000017500000033307511742733133022147 0ustar mathieumathieu/* Kapellmeister PIRL CVS ID: Kapellmeister.java,v 1.111 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Conductor; import PIRL.Conductor.Manager; import PIRL.Conductor.Management; import PIRL.Conductor.Colors; import PIRL.Configuration.*; import PIRL.PVL.Parameter; import PIRL.PVL.PVL_Exception; import PIRL.Viewers.View_Locator; import PIRL.Viewers.Dialog_Box; import PIRL.Viewers.Password_Dialog; import PIRL.Viewers.Icons; import PIRL.Viewers.Stream_Monitor; import PIRL.Viewers.Memory_Chart_Panel; import PIRL.Viewers.Parameter_View; import PIRL.Messenger.*; import PIRL.PVL.*; import PIRL.Utilities.Host; import PIRL.Strings.String_Buffer; import javax.swing.JFrame; import javax.swing.JRootPane; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JPopupMenu; import javax.swing.JMenuItem; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.JButton; import javax.swing.ImageIcon; import javax.swing.JComboBox; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionEvent; import javax.swing.JScrollPane; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.KeyStroke; import javax.swing.ToolTipManager; import javax.swing.Action; import javax.swing.AbstractAction; import javax.swing.SwingUtilities; import javax.swing.UIManager; import java.awt.Toolkit; import java.awt.Point; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.BorderLayout; import java.awt.Insets; import java.awt.Cursor; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; import java.net.InetSocketAddress; import java.io.File; import java.io.PrintStream; import java.io.IOException; import java.io.FileNotFoundException; import java.io.EOFException; import java.util.Collections; import java.util.List; import java.util.Vector; import java.util.Hashtable; /** A Kapellmeister is a management tool for Theaters where Conductors are located.

A Kapellmeister provides a GUI for the management of Theater locations. The Kapelmeister maintains a list of all Theaters it knows about along with the connected and disconnected status for the Stage_Manager at the Theater location. A summary status report from any connected Stage_Manager may be obtained for a selected Theater location.

The Stage_Manager at each Theater location is used to communicate with a set of Conductors assigned to the Theater. A Kapellmeister provides a table of all Conductors, and the Theater of each, along with the current processing status of each Conductor. A Profile table in the form of a matrix displays the counts of Conductors in each of the possible processing states, along with the total number of Conductors, for each unique combination of Theater and named Conductor, The name of a Conductor is typically the same as the name of the pipeline it is managing.

A Manager window may be opened onto any Conductor. The Manager offers detailed monitoring and management of the Conductor operation. A Kapellmeister provides basic start, stop, error reset, and quit controls that may be applied to selections of Conductors. The Kapelmeister can be used to define new Conductor instances to be run at any Theater location.

A file that defines the current Conductors and Theaters Profile may be generated by a Kapellmeister. A Profile file may be read by a Kapellmeister to produce the defined Conductor instances on all the specified Theaters. Defined Conductor instances that are not already present will be started; those that are already present will be quit, or stopped if they are currently running (they may optionally be aborted). Profile files, which are in Parameter Value Language text format, may be written by users as desired. Profile files that only specify Theater locations may be used to profide a Kapellmeister with a list of Theater locations to connect to and simply discover the Conductors that are operating there. A Profile file or a set of Theater locations may be specified on the Kapellmeister command line or interactively specified using the GUI.

All Conductor state information is provided directory from the operating Conductors via the Stage_Mangers; no Database tables or other intermediary information repositories are used. Conductor state information arrives and is posted to the Kapellmeister and Manager displays in real-time; no data polling cycles are used.

Connection to a Stage_Manager at a Theater location typically must be authenticated with a secure, public-private, encoded key exchange. The keys are based on a password - a character string of any length - that is known to the Stage_Manager and made known to a Kapellmeister.

A Configuration file is used to provide parameter values, including a default Stage_Manager password, that initialize various Stage_Manager communication variables and initial GUI control states.

@author Bradford Castalia - UA/PIRL @version 1.111 @see Theater @see Stage_Manager @see Profile */ public class Kapellmeister extends JFrame implements Message_Delivered_Listener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Kapellmeister (1.111 2012/04/16 06:04:11)"; /** The name included in the {@link #KAPELLMEISTER_IDENTITY} and used to {@link #Config_Pathname(String) find Configuration parameters}. */ public static final String KAPELLMEISTER_NAME = "Kapellmeister"; /** The Messenger client identity Message.

The identity contains the {@link #KAPELLMEISTER_NAME} {@link Message#Identity(String) Identity}, a {@link Message#CLASS_ID_PARAMETER_NAME} parameter with the {@link #ID} value, and a {@link Configuration#HOST} parameter with the {@link Host#FULL_HOSTNAME} value. */ public static final Message KAPELLMEISTER_IDENTITY = Message .Identity (KAPELLMEISTER_NAME) .Set (Message.CLASS_ID_PARAMETER_NAME, ID) .Set (Configuration.HOST, Host.FULL_HOSTNAME); // Configuration. /** The default Configuration source filename. */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Kapellmeister.conf"; private Configuration Kapellmeister_Configuration = null; public static final String THEATERS_PARAMETER_NAME = Profile.THEATERS_PARAMETER_NAME, THEATER_LOCATION_PARAMETER_NAME = Theater.THEATER_LOCATION_PARAMETER_NAME, ADDRESS_PARAMETER_NAME = Theater.ADDRESS_PARAMETER_NAME, THEATER_KEY_PARAMETER_NAME = Theater.KEY_PARAMETER_NAME, CONDUCTORS_PARAMETER_NAME = "Conductors", PIPELINE_PARAMETER_NAME = Conductor_Definition.PIPELINE_PARAMETER_NAME, CATALOG_PARAMETER_NAME = Conductor_Definition.CATALOG_PARAMETER_NAME, // Conductor identity uses a different name tban Conductor_Definition. CONFIGURATION_SOURCE_PARAMETER_NAME = Conductor_Definition.CONFIGURATION_SOURCE_PARAMETER_NAME, DATABASE_SERVER_PARAMETER_NAME = Conductor_Definition.SERVER_PARAMETER_NAME, CONFIRM_CONDUCTOR_ABORT_PARAMETER_NAME = "Confirm_Conductor_Abort", PROFILE_WAIT_TO_START_PARAMETER_NAME = "Profile_Wait_to_Start", PORT_PARAMETER_NAME = Stage_Manager.PORT_PARAMETER_NAME, RECEIVE_TIMEOUT_PARAMETER_NAME = "Receive_Timeout", PASSWORD_PARAMETER_NAME = Stage_Manager.PASSWORD_PARAMETER_NAME, TOOLTIPS_PARAMETER_NAME = Manager.TOOLTIPS_PARAMETER_NAME; private Parameter_View Configuration_View = null; // Main window. public static String Kapellmeister_Icon_Source = "Kapellmeister_Icon"; private static ImageIcon Kapellmeister_Icon = null; private static final int DEFAULT_WINDOW_LOCATION_X = 100, DEFAULT_WINDOW_LOCATION_Y = 50, DEFAULT_WINDOW_WIDTH = 700, DEFAULT_WINDOW_HEIGHT = 400, DEFAULT_LIST_WIDTH = 250, DEFAULT_LIST_HEIGHT = 100; private JCheckBoxMenuItem Tooltips_Checkbox; private static boolean DEFAULT_TOOLTIPS_ENABLED = true; // Theaters. private Hashtable Theater_Keys = new Hashtable (); private String Skeleton_Key = null; /** Value used to display the Configuration password parameters. */ public static final String MASKED_PASSWORD = "****"; // Theater locations displayed by the Theater_List. private Theater_List_Model Theaters = new Theater_List_Model (); private Theater_List Theater_List; private ItemListener Theater_List_ItemListener; private Action Theater_Status_Action = null; private static final int MAX_VISIBLE_THEATERS = 20; private JButton Open_Theater_Button; private static final String OPEN_THEATER_ICON_NAME = "Open_Theater", CLOSE_THEATER_ICON_NAME = "Close_Theater"; private static ImageIcon Open_Theater_Icon = null, Close_Theater_Icon = null; // Conductors. /** The name of Conductor identities from the Stage_Manager. */ public static final String CONDUCTOR_NAME = Conductor.CONDUCTOR_GROUP; // Conductor identities displayed by the Conductor_Table. private Conductor_Table_Model Conductors = new Conductor_Table_Model (); // Table of Conductors reported by the Stage_Manager. private Conductor_Table Conductor_Table; private Conductor_Table_ListSelectionListener Conductor_Table_Selection_Listener; // Conductors menu: private JPopupMenu Conductor_Table_Popup_Menu; private Action New_Conductor_Action = null, Open_Conductor_Action = null, Close_Conductor_Action = null, Start_Conductor_Action = null, Reset_Conductor_Action = null, Stop_Conductor_Action = null, Quit_Conductor_Action = null; private JCheckBoxMenuItem Confirm_Conductor_Abort_Checkbox, Wait_to_Start_Checkbox; private boolean DEFAULT_CONFIRM_CONDUCTOR_ABORT = true, DEFAULT_PROFILE_WAIT_TO_START = true; private New_Conductor_Dialog New_Conductor_Dialog; // Table of Theater by Conductor counts. private Conductor_Matrix_Model Matrix; private Conductor_Matrix Conductor_Matrix; // Profile menu: private JPopupMenu Conductor_Matrix_Popup_Menu; private JCheckBoxMenuItem Profile_Running_Count_Checkbox, Profile_Running_Count_Checkbox_Popup, Profile_Polling_Count_Checkbox, Profile_Polling_Count_Checkbox_Popup, Profile_Waiting_Count_Checkbox, Profile_Waiting_Count_Checkbox_Popup, Profile_Halted_Count_Checkbox, Profile_Halted_Count_Checkbox_Popup, Profile_Count_Annotate_Checkbox, Profile_Count_Annotate_Checkbox_Popup; // Current Conductor Matrix Profile. private Profile Current_Profile = null; // The pathname to the most recent Current_Profile. private String Profile_Pathname = null; private JFileChooser File_Chooser = new JFileChooser (System.getProperty ("user.dir")); // Monitor of communications and events with the Stage_Manager. private JFrame Monitor_Window; private Stream_Monitor Monitor; private PrintStream Report; private Memory_Chart_Panel Memory_Panel; private Parameter_View Theater_Status_Report_View = null; private JCheckBoxMenuItem Monitor_Reports_Checkbox, Memory_Monitor_Checkbox; /** Message monitor auto-styling attributes. */ public static final SimpleAttributeSet MESSAGE_DELIVERED_STYLE = new SimpleAttributeSet (), MESSAGE_SENT_STYLE = new SimpleAttributeSet (); static { StyleConstants.setFontFamily (MESSAGE_DELIVERED_STYLE, "Monospaced"); StyleConstants.setBold (MESSAGE_DELIVERED_STYLE, true); StyleConstants.setBackground (MESSAGE_DELIVERED_STYLE, Colors.MESSAGE_DELIVERED); StyleConstants.setFontFamily (MESSAGE_SENT_STYLE, "Monospaced"); StyleConstants.setBold (MESSAGE_SENT_STYLE, true); StyleConstants.setBackground (MESSAGE_SENT_STYLE, Colors.MESSAGE_SENT); } /** Success exit status (0). */ public static final int EXIT_SUCCESS = 0; /** Command line syntax problem exit status (1). */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** The log file could not be opened for writing exit status. */ public static final int EXIT_CONFIG_FILE_PROBLEM = 2; /** A fatal unrecoverable error was encountered. */ public static final int EXIT_UNRECOVERABLE_ERROR = 9; /** An unexpected exception occured. */ public static final int EXIT_UNEXPECTED_EXCEPTION = 10; // Miscellaneous. private static final Toolkit TOOLKIT = Toolkit.getDefaultToolkit (); /* The menu accelerator key mask. This is the Control key by default. It is the Command key on Mac systems. */ private static final int MENU_SHORTCUT_KEY_MASK = TOOLKIT.getMenuShortcutKeyMask (); private static final Cursor DEFAULT_CURSOR = Cursor.getPredefinedCursor (Cursor.DEFAULT_CURSOR), WAIT_CURSOR = Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR); private static String NL = Stage_Manager.NL; private static final char FILE_SEPARATOR = System.getProperty ("file.separator").charAt (0); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_THEATER_CONNECT = 1 << 3, DEBUG_MESSAGES = 1 << 4, DEBUG_DONE = 1 << 5, DEBUG_IDENTITIES = 1 << 6, DEBUG_NEW_CONDUCTOR = 1 << 7, DEBUG_CONDUCTOR_CONNECT = 1 << 8, DEBUG_PROFILE = 1 << 9, DEBUG_SELECTION = 1 << 10, DEBUG_ICONS = 1 << 11, DEBUG_MAIN = 1 << 12, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Kapellmeister using the default configuration.

The {@link #DEFAULT_CONFIGURATION_FILENAME} will be */ public Kapellmeister () throws Configuration_Exception {this (null, null);} public Kapellmeister ( Vector sources ) throws Configuration_Exception {this (sources, null);} public Kapellmeister ( Configuration configuration ) throws Configuration_Exception {this (null, configuration);} public Kapellmeister ( Vector sources, Configuration configuration ) throws Configuration_Exception { super ("Kapellmeister"); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Kapellmeister"); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Configure ..."); Configure (configuration); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Load_Icons ..."); Load_Icons (); if (Kapellmeister_Icon != null) setIconImage (Kapellmeister_Icon.getImage ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Menus ..."); setJMenuBar (Menus ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Panels ..."); setContentPane (Panels ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" New_Conductor_Dialog ..."); New_Conductor_Dialog = new New_Conductor_Dialog ("New Conductor", this); New_Conductor_Dialog.Wait_to_Start (Wait_to_Start_Checkbox.isSelected ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Monitor_Window ..."); Monitor_Window (); pack (); setSize (new Dimension (DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); setLocation (DEFAULT_WINDOW_LOCATION_X, DEFAULT_WINDOW_LOCATION_Y); setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) {Exit (EXIT_SUCCESS);} public void windowClosed (WindowEvent event) {Exit (EXIT_SUCCESS);} }); if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Setting the Kapellmeister visible"); setVisible (true); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Writing ID to Monitor ..."); Report (ID + NL + NL); if (sources != null) { if (sources.size () == 1 && new File ((String)sources.get (0)).isFile ()) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Load_Profile " + (String)sources.get (0) + " ..."); Load_Profile ((String)sources.get (0)); } else { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Open_Theaters ..."); Open_Theaters (sources); } } if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Kapellmeister"); } /*============================================================================== Accessors */ /** Get the default Stage_Manager socket communications port number.

@return The communications port number. @see #Default_Theater_Port(int) */ public int Default_Theater_Port () {return Theater.Default_Port ();} /** Set the default Stage_Manager socket communications port number.

@param port The {@link Theater#Default_Port(int) default port} for Stage_Manager communications. @return This Kapellmeister. */ public Kapellmeister Default_Theater_Port ( int port ) {Theater.Default_Port (port); return this;} /** Get the maximum amount of time, in seconds, that will be used when waiting for a Theater protocol Message to be received.

@return The timeout value. @see #Receive_Timeout(int) */ public int Receive_Timeout () {return Theater.Default_Receive_Timeout ();} /** Set the maximum amount of time, in seconds, that will be used when waiting for a Theater protocol Message to be received.

@param timeout The {@link Theater#Default_Receive_Timeout(int) default timeout value} which is also applied to all Theaters in the Theaters list. @return This Kapellmeister. */ public Kapellmeister Receive_Timeout ( int timeout ) { Theater.Default_Receive_Timeout (timeout); Theaters.Receive_Timeout (timeout); return this; } /*============================================================================== Configuration */ /** Configure the Kapellmeister.

Parameter names are case insensitive. All parameters are optional. Parameters will first be sought in the {@link #KAPELLMEISTER_NAME} group.

{@link #PORT_PARAMETER_NAME}
The {@link #Default_Theater_Port(int) default Stage_Manager communcations socket port number}.
{@link #RECEIVE_TIMEOUT_PARAMETER_NAME}
The {@link #Receive_Timeout(int) maximum amount of time, in seconds, that will be used when waiting for a Theater protocol Message to be received}.
{@link Stage_Manager#PASSWORD_PARAMETER_NAME}
The password required by the Stage_Manager to authenticate client connections. If {@link Stage_Manager#UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true unauthenticated connections with no password will be accepted.
{@link #TOOLTIPS_PARAMETER_NAME}
If true, enabled, yes, on, or 1 GUI tooltips will be enabled.

@param configuration A Configuration. If null, an attempt will be made to obtain a Configuration using the {@link #DEFAULT_CONFIGURATION_FILENAME} source. */ public void Configure ( Configuration configuration ) throws Configuration_Exception { if (configuration == null) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Trying " + DEFAULT_CONFIGURATION_FILENAME); try {configuration = new Configuration (DEFAULT_CONFIGURATION_FILENAME);} catch (IllegalArgumentException exception) { // No configuration file; use a default. if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Using a default Configuration"); configuration = new Configuration (); } } Kapellmeister_Configuration = configuration; Kapellmeister_Configuration.Case_Sensitive (false); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Configuration -" + NL + Kapellmeister_Configuration.Description ()); try {Kapellmeister_Configuration.Insert (new Parameter (Local_Theater.CLASS_ID_PARAMETER_NAME).Value (ID), 0);} catch (PVL_Exception exception) {} // Stage_Manager Default_Theater_Port ((int)Kapellmeister_Configuration.Get_Number (Config_Pathname (PORT_PARAMETER_NAME), Theater.Default_Port ())); Config_Value (PORT_PARAMETER_NAME, new Integer (Theater.Default_Port ())); Receive_Timeout ((int)Kapellmeister_Configuration.Get_Number (Config_Pathname (RECEIVE_TIMEOUT_PARAMETER_NAME), Theater.Default_Receive_Timeout ())); Config_Value (RECEIVE_TIMEOUT_PARAMETER_NAME, new Integer (Theater.Default_Receive_Timeout ())); Skeleton_Key = Config_Value (Stage_Manager.PASSWORD_PARAMETER_NAME); if (Skeleton_Key != null && Skeleton_Key.length () == 0) Skeleton_Key = null; // Mask out all password values. Kapellmeister_Configuration.Set_All ("PASSWORD", MASKED_PASSWORD); } /** Get the String value of a configuration parameter.

@param name The name of the parameter from which to obtain the value. If the name is not an absolute pathname it will be {@link #Config_Pathname(String) qualified} before use. @return A String value. This will be null if the parameter could not be found in the {@link #Configure(Configuration) configuration}. */ public String Config_Value ( String name ) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">-< Config_Value: " + name + " = " + Kapellmeister_Configuration.Get_One (Config_Pathname (name))); return Kapellmeister_Configuration.Get_One (Config_Pathname (name)); } /** Get the boolean value of a configuration parameter.

@param name The name of the parameter from which to obtain the value. If the name is not an absolute pathname it will be {@link #Config_Pathname(String) qualified} before use. @param default_value The default value to be returned if the named parameter is not present in the Configuration. @return The {@link Configuration#Enabled(String, boolean) enabled} state of the parameter, or the default value if the parameter is not present in the Configuration. */ public boolean Config_Flag ( String name, boolean default_value ) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">-< Config_Flag: " + name + " = " + Kapellmeister_Configuration.Enabled (Config_Pathname (name), default_value)); return Kapellmeister_Configuration.Enabled (Config_Pathname (name), default_value); } /** Set a parameter in the configuration.

The parameter is set in the {@link #Config_Pathname(String) parameter group} appropriate for the application.

@param name The name of the Assignment parameter to have its value set. If the name is not an absolute pathname it will be {@link #Config_Pathname(String) qualified} before use. @param value An Object to use for the parameter's value. : If null, the parameter will have no value; it will be a Token. @return true if an existing parameter by the same name was replaced; false if the parameter is being set for the first time. @throws Configuration_Exception If there was a problem setting the parameter. */ protected boolean Config_Value ( String name, Object value ) throws Configuration_Exception { try {return Kapellmeister_Configuration.Set (Config_Pathname (name), value);} catch (Configuration_Exception exception) { throw new Configuration_Exception (ID + NL +"Unable to set configuration parameter: " + name + " = " + value + NL + exception.getMessage ()); } } /** Get the configuration pathname for a parameter name.

If the name is not an {@link Configuration#Is_Absolute_Pathname(String) absolute pathname} the {@link #KAPELLMEISTER_NAME} group name is prepended to the name to form an absolute pathname.

@param name A parameter name String. @return The possibily modified abosulte pathname String. */ public String Config_Pathname ( String name ) { if (name != null && ! Configuration.Is_Absolute_Pathname (name)) return Kapellmeister_Configuration.Path_Delimiter () + KAPELLMEISTER_NAME + Kapellmeister_Configuration.Path_Delimiter () + name; return name; } /*============================================================================== GUI */ private JMenuBar Menus () { JMenuBar menu_bar = new JMenuBar (); JMenu menu; JMenuItem menu_item; // File. menu = new JMenu ("File"); menu.setMnemonic (KeyEvent.VK_F); menu_item = new JMenuItem ("Open Profile ..."); menu_item.setMnemonic (KeyEvent.VK_O); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_O, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Select_Profile ();}}); menu.add (menu_item); menu_item = new JMenuItem ("Save"); menu_item.setMnemonic (KeyEvent.VK_S); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_S, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Profile (false);}}); menu.add (menu_item); menu_item = new JMenuItem ("Save As ..."); menu_item.setMnemonic (KeyEvent.VK_A); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Profile (true);}}); menu.add (menu_item); File_Chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); File_Chooser.setFileHidingEnabled (false); menu.addSeparator (); menu_item = new JMenuItem ("Exit"); menu_item.setMnemonic (KeyEvent.VK_X); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_Q, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Exit (EXIT_SUCCESS);}}); menu.add (menu_item); menu_bar.add (menu); // View. menu = new JMenu ("View"); menu.setMnemonic (KeyEvent.VK_V); menu_item = new JMenuItem ("Configuration"); menu_item.setMnemonic (KeyEvent.VK_C); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_C, ActionEvent.ALT_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {View_Configuration ();}}); menu.add (menu_item); Theater_Status_Action = new AbstractAction ("Theater Status") {public void actionPerformed (ActionEvent event) {Theater_Status_Report ();}}; Theater_Status_Action.setEnabled (false); Theater_Status_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_S); Theater_Status_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_S, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Theater_Status_Action)); menu_item = new JMenuItem ("Monitor"); menu_item.setMnemonic (KeyEvent.VK_M); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_M, ActionEvent.ALT_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Show_Monitor ();}}); menu.add (menu_item); menu.addSeparator (); Tooltips_Checkbox = new JCheckBoxMenuItem ("Tooltips", Config_Flag (TOOLTIPS_PARAMETER_NAME, DEFAULT_TOOLTIPS_ENABLED)); Tooltips_Checkbox.setMnemonic (KeyEvent.VK_T); Tooltips_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {ToolTipManager.sharedInstance ().setEnabled (Tooltips_Checkbox.isSelected ());}}); menu.add (Tooltips_Checkbox); menu_bar.add (menu); // Conductors. menu = new JMenu ("Conductors"); menu.setMnemonic (KeyEvent.VK_C); New_Conductor_Action = new AbstractAction ("New ...") {public void actionPerformed (ActionEvent event) {New_Conductor ();}}; New_Conductor_Action.setEnabled (false); New_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_N); New_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_N, MENU_SHORTCUT_KEY_MASK)); menu.add (new JMenuItem (New_Conductor_Action)); menu.addSeparator (); Open_Conductor_Action = new AbstractAction ("Open") {public void actionPerformed (ActionEvent event) {Open_Manager ();}}; Open_Conductor_Action.setEnabled (false); Open_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_O); Open_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_O, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Open_Conductor_Action)); Close_Conductor_Action = new AbstractAction ("Close") {public void actionPerformed (ActionEvent event) {Close_Manager ();}}; Close_Conductor_Action.setEnabled (false); Close_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_C); Close_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_W, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Close_Conductor_Action)); menu.addSeparator (); Start_Conductor_Action = new AbstractAction ("Start") {public void actionPerformed (ActionEvent event) {Start_Conductor ();}}; Start_Conductor_Action.setEnabled (false); Start_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_T); Start_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_T, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Start_Conductor_Action)); Reset_Conductor_Action = new AbstractAction ("Reset") {public void actionPerformed (ActionEvent event) {Reset_Conductor ();}}; Reset_Conductor_Action.setEnabled (false); Reset_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_R); Reset_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_R, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Reset_Conductor_Action)); Stop_Conductor_Action = new AbstractAction ("Stop") {public void actionPerformed (ActionEvent event) {Stop_Conductor ();}}; Stop_Conductor_Action.setEnabled (false); Stop_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_P); Stop_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_P, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Stop_Conductor_Action)); Quit_Conductor_Action = new AbstractAction ("Quit") {public void actionPerformed (ActionEvent event) {Quit_Conductor ();}}; Quit_Conductor_Action.setEnabled (false); Quit_Conductor_Action.putValue (Action.MNEMONIC_KEY, KeyEvent.VK_Q); Quit_Conductor_Action.putValue (Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke (KeyEvent.VK_Q, ActionEvent.ALT_MASK)); menu.add (new JMenuItem (Quit_Conductor_Action)); menu.addSeparator (); Confirm_Conductor_Abort_Checkbox = new JCheckBoxMenuItem ("Confirm Abort", Config_Flag (CONFIRM_CONDUCTOR_ABORT_PARAMETER_NAME, DEFAULT_CONFIRM_CONDUCTOR_ABORT)); menu.add (Confirm_Conductor_Abort_Checkbox); Wait_to_Start_Checkbox = new JCheckBoxMenuItem ("Wait-to-Start", Config_Flag (PROFILE_WAIT_TO_START_PARAMETER_NAME, DEFAULT_PROFILE_WAIT_TO_START)); Wait_to_Start_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {New_Conductor_Dialog.Wait_to_Start (Wait_to_Start_Checkbox.isSelected ());}}); menu.add (Wait_to_Start_Checkbox); menu_bar.add (menu); // Profile. menu = new JMenu ("Profile"); menu.setMnemonic (KeyEvent.VK_P); Profile_Running_Count_Checkbox = new JCheckBoxMenuItem ("Running", true); Profile_Running_Count_Checkbox.setBackground (Colors.RUNNING_STATE); Profile_Running_Count_Checkbox.setMnemonic (KeyEvent.VK_R); Profile_Running_Count_Checkbox.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_R, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Running_Count_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Running_Count_Checkbox_Popup.setState (Profile_Running_Count_Checkbox.getState ()); Profile_Counts ();}}); menu.add (Profile_Running_Count_Checkbox); Profile_Polling_Count_Checkbox = new JCheckBoxMenuItem ("Polling", true); Profile_Polling_Count_Checkbox.setBackground (Colors.POLLING_STATE); Profile_Polling_Count_Checkbox.setMnemonic (KeyEvent.VK_P); Profile_Polling_Count_Checkbox.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_P, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Polling_Count_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Polling_Count_Checkbox_Popup.setState (Profile_Polling_Count_Checkbox.getState ()); Profile_Counts ();}}); menu.add (Profile_Polling_Count_Checkbox); Profile_Waiting_Count_Checkbox = new JCheckBoxMenuItem ("Waiting", true); Profile_Waiting_Count_Checkbox.setBackground (Colors.WAITING_STATE); Profile_Waiting_Count_Checkbox.setMnemonic (KeyEvent.VK_W); Profile_Waiting_Count_Checkbox.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_W, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Waiting_Count_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Waiting_Count_Checkbox_Popup.setState (Profile_Waiting_Count_Checkbox.getState ()); Profile_Counts ();}}); menu.add (Profile_Waiting_Count_Checkbox); Profile_Halted_Count_Checkbox = new JCheckBoxMenuItem ("Halted", true); Profile_Halted_Count_Checkbox.setBackground (Colors.HALTED_STATE); Profile_Halted_Count_Checkbox.setMnemonic (KeyEvent.VK_H); Profile_Halted_Count_Checkbox.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_H, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Halted_Count_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Halted_Count_Checkbox_Popup.setState (Profile_Halted_Count_Checkbox.getState ()); Profile_Counts ();}}); menu.add (Profile_Halted_Count_Checkbox); menu.addSeparator (); Profile_Count_Annotate_Checkbox = new JCheckBoxMenuItem ("Annotate", false); Profile_Count_Annotate_Checkbox.setMnemonic (KeyEvent.VK_A); Profile_Count_Annotate_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Count_Annotate_Checkbox_Popup.setState (Profile_Count_Annotate_Checkbox.getState ()); Conductor_Matrix.State_Annotate (Profile_Count_Annotate_Checkbox.isSelected ());}}); menu.add (Profile_Count_Annotate_Checkbox); menu_bar.add (menu); return menu_bar; } private JPanel Panels () { JPanel panel = new JPanel (new GridBagLayout ()), table_panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 5, 5, 5); table_panel.add (Theaters_Panel (), location); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (0, 5, 5, 5); table_panel.add (Conductor_Table_Panel (), location); JSplitPane split_pane = new JSplitPane (JSplitPane.HORIZONTAL_SPLIT, table_panel, Conductor_Matrix_Panel ()); split_pane.setOneTouchExpandable (true); panel.add (split_pane, location); return panel; } /*------------------------------------------------------------------------------ Theaters */ private JPanel Theaters_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); panel.setBorder (BorderFactory.createTitledBorder (THEATERS_PARAMETER_NAME)); // Open(ed)/Close(d) button: Open_Theater_Button = new JButton (); Open_Theater_Button.setBorderPainted (false); Open_Theater_Button.setContentAreaFilled (false); if (Open_Theater_Icon != null) Open_Theater_Button.setPreferredSize (new Dimension (Open_Theater_Icon.getIconWidth (), Open_Theater_Icon.getIconHeight ())); Theater_is_Open (false); Open_Theater_Button.setEnabled (false); Open_Theater_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_Theater ();}}); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 3, 0, 4); panel.add (Open_Theater_Button, location); // List: Theater_List = new Theater_List (Theaters); Theater_List.setMaximumRowCount (MAX_VISIBLE_THEATERS); Theater_List.addItemListener (Theater_List_ItemListener = new ItemListener () {public void itemStateChanged (ItemEvent event) {if (event.getStateChange () == ItemEvent.SELECTED) Theater_Selected ();}}); location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (0, 0, 0, 0); panel.add (Theater_List, location); return panel; } /*------------------------------------------------------------------------------ Conductors */ private JPanel Conductor_Table_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); panel.setBorder (BorderFactory.createTitledBorder (CONDUCTORS_PARAMETER_NAME)); Conductor_Table = new Conductor_Table (Conductors); Conductor_Table.addMouseListener (new Conductor_Table_Mouse_Listener ()); Conductor_Table.getSelectionModel ().addListSelectionListener (Conductor_Table_Selection_Listener = new Conductor_Table_ListSelectionListener ()); JScrollPane table_pane = new JScrollPane (Conductor_Table); table_pane.setMinimumSize (new Dimension (100, 100)); table_pane.setPreferredSize (new Dimension (DEFAULT_LIST_WIDTH, DEFAULT_LIST_HEIGHT)); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (table_pane, location); // Popup menu: Conductor_Table_Popup_Menu = new JPopupMenu (); Conductor_Table_Popup_Menu.add (new JMenuItem (New_Conductor_Action)); Conductor_Table_Popup_Menu.addSeparator (); Conductor_Table_Popup_Menu.add (new JMenuItem (Open_Conductor_Action)); Conductor_Table_Popup_Menu.add (new JMenuItem (Close_Conductor_Action)); Conductor_Table_Popup_Menu.addSeparator (); Conductor_Table_Popup_Menu.add (new JMenuItem (Start_Conductor_Action)); Conductor_Table_Popup_Menu.add (new JMenuItem (Reset_Conductor_Action)); Conductor_Table_Popup_Menu.add (new JMenuItem (Stop_Conductor_Action)); Conductor_Table_Popup_Menu.add (new JMenuItem (Quit_Conductor_Action)); Conductor_Table_Popup_Menu.addSeparator (); Conductor_Table_Popup_Menu.add (new JMenuItem (Theater_Status_Action)); return panel; } private class Conductor_Table_ListSelectionListener implements ListSelectionListener { public void valueChanged ( ListSelectionEvent event ) { if (event.getValueIsAdjusting ()) return; if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">>> Kapellmeister.Conductor_Table_ListSelectionListener.valueChanged"); int row = Conductor_Table.getSelectedRow (); if (row >= 0) { row = Conductor_Table.convertRowIndexToModel (row); // Select the corresponding Theater. if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Selecting theater at location: " + Conductors.Location (row)); Theater_Selected (Conductors.Location (row), false); } Refresh_Conductor_Ops_Menus (); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println ("<<< Kapellmeister.Conductor_Table_ListSelectionListener.valueChanged"); } } /*.............................................................................. Conductor_Table_Mouse_Listener */ /** MouseListener for the Conductor_Table. */ private class Conductor_Table_Mouse_Listener extends MouseAdapter { public void mousePressed (MouseEvent event) {Popup (event);} public void mouseReleased (MouseEvent event) {Popup (event);} private void Popup ( MouseEvent event ) { if (event.isPopupTrigger ()) Conductor_Table_Popup_Menu.show (event.getComponent (), event.getX (), event.getY ()); } public void mouseClicked ( MouseEvent event ) { if (event.getButton () == MouseEvent.BUTTON1) { if (event.getClickCount () == 2) Open_Manager (); else if (event.isShiftDown () && event.isControlDown ()) { Conductor_Table table = (Conductor_Table)event.getSource (); int row = table.rowAtPoint (event.getPoint ()); if (row >= 0) { if (table.columnAtPoint (event.getPoint ()) == 0) Select_Conductors_at (Conductors.Location (row)); else Select_Conductors_for (Conductors.Conductor_Name (row)); } } } } } // Conductor_Table_Mouse_Listener /*------------------------------------------------------------------------------ Conductor Matrix */ private JPanel Conductor_Matrix_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); panel.setBorder (BorderFactory.createTitledBorder ("Profile")); Matrix = new Conductor_Matrix_Model (Conductors); Conductor_Matrix = new Conductor_Matrix (Matrix); Conductor_Matrix.addMouseListener (new Conductor_Matrix_Mouse_Listener ()); Conductor_Matrix.getSelectionModel () .addListSelectionListener (new ListSelectionListener () {public void valueChanged (ListSelectionEvent event) {if (! event.getValueIsAdjusting ()) Matrix_Selected ();}}); JScrollPane table_pane = new JScrollPane (Conductor_Matrix); table_pane.setMinimumSize (new Dimension (100, 100)); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (table_pane, location); // Popup menu: Conductor_Matrix_Popup_Menu = new JPopupMenu ("Conductor State Counts"); Profile_Running_Count_Checkbox_Popup = new JCheckBoxMenuItem ("Running", true); Profile_Running_Count_Checkbox_Popup.setBackground (Colors.RUNNING_STATE); Profile_Running_Count_Checkbox_Popup.setMnemonic (KeyEvent.VK_R); Profile_Running_Count_Checkbox_Popup.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_R, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Running_Count_Checkbox_Popup.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Running_Count_Checkbox.setState (Profile_Running_Count_Checkbox_Popup.getState ()); Profile_Counts ();}}); Conductor_Matrix_Popup_Menu.add (Profile_Running_Count_Checkbox_Popup); Profile_Polling_Count_Checkbox_Popup = new JCheckBoxMenuItem ("Polling", true); Profile_Polling_Count_Checkbox_Popup.setBackground (Colors.POLLING_STATE); Profile_Polling_Count_Checkbox_Popup.setMnemonic (KeyEvent.VK_P); Profile_Polling_Count_Checkbox_Popup.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_P, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Polling_Count_Checkbox_Popup.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Polling_Count_Checkbox.setState (Profile_Polling_Count_Checkbox_Popup.getState ()); Profile_Counts ();}}); Conductor_Matrix_Popup_Menu.add (Profile_Polling_Count_Checkbox_Popup); Profile_Waiting_Count_Checkbox_Popup = new JCheckBoxMenuItem ("Waiting", true); Profile_Waiting_Count_Checkbox_Popup.setBackground (Colors.WAITING_STATE); Profile_Waiting_Count_Checkbox_Popup.setMnemonic (KeyEvent.VK_W); Profile_Waiting_Count_Checkbox_Popup.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_W, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Waiting_Count_Checkbox_Popup.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Waiting_Count_Checkbox.setState (Profile_Waiting_Count_Checkbox_Popup.getState ()); Profile_Counts ();}}); Conductor_Matrix_Popup_Menu.add (Profile_Waiting_Count_Checkbox_Popup); Profile_Halted_Count_Checkbox_Popup = new JCheckBoxMenuItem ("Halted", true); Profile_Halted_Count_Checkbox_Popup.setBackground (Colors.HALTED_STATE); Profile_Halted_Count_Checkbox_Popup.setMnemonic (KeyEvent.VK_H); Profile_Halted_Count_Checkbox_Popup.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_H, ActionEvent.ALT_MASK | ActionEvent.SHIFT_MASK)); Profile_Halted_Count_Checkbox_Popup.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Halted_Count_Checkbox.setState (Profile_Halted_Count_Checkbox_Popup.getState ()); Profile_Counts ();}}); Conductor_Matrix_Popup_Menu.add (Profile_Halted_Count_Checkbox_Popup); Conductor_Matrix_Popup_Menu.addSeparator (); Profile_Count_Annotate_Checkbox_Popup = new JCheckBoxMenuItem ("Annotate", false); Profile_Count_Annotate_Checkbox_Popup.setMnemonic (KeyEvent.VK_A); Profile_Count_Annotate_Checkbox_Popup.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Profile_Count_Annotate_Checkbox.setState (Profile_Count_Annotate_Checkbox_Popup.getState ()); Conductor_Matrix.State_Annotate (Profile_Count_Annotate_Checkbox_Popup.isSelected ());}}); Conductor_Matrix_Popup_Menu.add (Profile_Count_Annotate_Checkbox_Popup); return panel; } private void Profile_Counts () { int counts_shown = 0; if (Profile_Running_Count_Checkbox.isSelected ()) counts_shown |= Conductor_Matrix.RUNNING_COUNT; if (Profile_Polling_Count_Checkbox.isSelected ()) counts_shown |= Conductor_Matrix.POLLING_COUNT; if (Profile_Waiting_Count_Checkbox.isSelected ()) counts_shown |= Conductor_Matrix.WAITING_COUNT; if (Profile_Halted_Count_Checkbox.isSelected ()) counts_shown |= Conductor_Matrix.HALTED_COUNT; // Always show Totals. counts_shown |= Conductor_Matrix.TOTAL_COUNT; Conductor_Matrix.Counts_Shown (counts_shown); } /*.............................................................................. Conductor_Matrix_Mouse_Listener */ /** MouseListener for the Conductor_Matrix table. */ private class Conductor_Matrix_Mouse_Listener extends MouseAdapter { public void mousePressed (MouseEvent event) {Popup (event);} public void mouseReleased (MouseEvent event) {Popup (event);} private void Popup ( MouseEvent event ) { if (event.isPopupTrigger ()) Conductor_Matrix_Popup_Menu.show (event.getComponent (), event.getX (), event.getY ()); } public void mouseClicked ( MouseEvent event ) { if (event.getButton () == MouseEvent.BUTTON1) { } } } // Conductor_Matrix_Mouse_Listener /*------------------------------------------------------------------------------ Monitor */ private void Monitor_Window () { Monitor_Window = new JFrame ("Kapellmeister Monitor"); JPanel monitor_panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); Monitor = new Stream_Monitor () .Auto_Style (true) .Auto_Style ("==>", Monitor.HIGHLIGHT_STYLE) .Auto_Style ("!!!", Monitor.NOTICE_STYLE) .Auto_Style ("<<<", MESSAGE_DELIVERED_STYLE) .Auto_Style (">>>", MESSAGE_SENT_STYLE); Monitor.setPreferredSize (new Dimension (DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)); Report = new PrintStream (Monitor.Stream (), true); JMenuBar menu_bar = new JMenuBar (); JMenu menu; // File menu. menu = Monitor.File_Menu (); menu.addSeparator (); JMenuItem menu_item = new JMenuItem ("Close"); menu_item.setMnemonic (KeyEvent.VK_C); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_W, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Monitor_Window.setVisible (false);}}); menu.add (menu_item); menu_bar.add (menu); // View menu. menu = Monitor.View_Menu (); menu.addSeparator (); Monitor_Reports_Checkbox = new JCheckBoxMenuItem ("Reports", false); menu.add (Monitor_Reports_Checkbox); Memory_Monitor_Checkbox = new JCheckBoxMenuItem ("Memory Monitor", false); Memory_Monitor_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Memory_Panel.Run (((JCheckBoxMenuItem)event.getSource ()).isSelected ());}}); menu.add (Memory_Monitor_Checkbox); menu_bar.add (menu); Monitor_Window.setJMenuBar (menu_bar); // Reports pane. Monitor.setBorder (BorderFactory.createTitledBorder ("Reports")); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; monitor_panel.add (Monitor, location); // Memory monitor pane. Memory_Panel = new Memory_Chart_Panel (1, true); Memory_Panel.Run (Memory_Monitor_Checkbox.isSelected ()); Memory_Panel.setBorder (BorderFactory.createTitledBorder ("Memory Monitor")); location.weighty = 0.0; monitor_panel.add (Memory_Panel, location); // Tie the Start/Stop popup menu item to the View Memory Monitor menu item. JPopupMenu popup_menu = Memory_Panel.Menu (); menu_item = (JMenuItem)popup_menu.getComponent (0); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Memory_Monitor_Checkbox.setSelected (! Memory_Monitor_Checkbox.isSelected ());}}); Monitor_Window.getContentPane ().add (monitor_panel, BorderLayout.CENTER); Monitor_Window.pack (); Dimension size = Memory_Panel.getPreferredSize (); Memory_Panel.setPreferredSize (size); size.width = 30; Memory_Panel.setMinimumSize (size); Monitor_Window.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); Monitor_Window.addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) {Monitor_Window.setVisible (false);} public void windowClosed (WindowEvent event) {Monitor_Window.setVisible (false);} }); } private void Show_Monitor () { if (! Monitor_Window.isVisible ()) { MONITOR_LOCATOR.Relocate (Monitor_Window, this); Monitor_Window.setVisible (true); } Monitor_Window.toFront (); } private static final View_Locator MONITOR_LOCATOR = new View_Locator () .Offsets (0, 0) .Vertical (View_Locator.BOTTOM | View_Locator.OUTWARD) .Horizontal (View_Locator.LEFT | View_Locator.INWARD); private void Report ( String report ) { if (Monitor_Reports_Checkbox.isSelected ()) { final String reported = report; SwingUtilities.invokeLater (new Runnable () {public void run () {Report.println (reported);}}); } } /*============================================================================== Actions */ private void View_Configuration () { if (Configuration_View == null) { Configuration_View = new Parameter_View ("Kapellmeister", Kapellmeister_Configuration); Configuration_View.setDefaultCloseOperation (JFrame.HIDE_ON_CLOSE); View_Locator locator = new View_Locator () .Offsets (0, 0) .Vertical (View_Locator.TOP | View_Locator.INWARD) .Horizontal (View_Locator.RIGHT | View_Locator.OUTWARD); locator.Relocate (Configuration_View, this); Configuration_View.setVisible (true); } else { Configuration_View.Parameter_Pane () .Parameter (Kapellmeister_Configuration); Configuration_View.setVisible (true); Configuration_View.toFront (); } } /*------------------------------------------------------------------------------ Profiles */ private void Select_Profile () { if (File_Chooser.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { File file = File_Chooser.getSelectedFile (); if (! file.isFile ()) { Dialog_Box.Notice ("Can't open file " + file.getAbsolutePath () + '\n' +"No such file.", this); return; } try {CWD (file);} catch (FileNotFoundException exception) {} String pathname = null; try {pathname = file.getCanonicalPath ();} catch (Exception exception) {} Load_Profile (pathname); } } private void Load_Profile ( String source ) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (">>> Kapellmeister.Load_Profile: " + source); if (source == null) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println ("<<< Kapellmeister.Load_Profile"); return; } String report = null; Exception exception = null; try {Current_Profile = new Profile (source);} catch (Configuration_Exception except) { report = "There is a configuration problem in the Profile -" + NL + source + NL + except.getMessage (); exception = except; } catch (PVL_Exception except) { report = "There is a PVL syntax problem in the Profile -" + NL + source + NL + except + NL + except.getMessage (); exception = except; } if (report != null) { Report ("!!! " + report); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" " + report); new Error_Report ("Load Profile Failure", report, exception, this); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println ("<<< Kapellmeister.Load_Profile"); return; } if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Profile -" + NL + Current_Profile); // Set the most recent Profile pathname and CWD. File file = new File (source); Profile_Pathname = file.getAbsolutePath (); try {CWD (file);} catch (Exception except) {/* Already found */} Vector> theater_definitions = Current_Profile.Theater_Definitions (); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Theater_Definitions: " + theater_definitions); while (theater_definitions.size () > 0) { String theater_location = (String)theater_definitions.get (0) .get (Profile.THEATER_LOCATION_INDEX); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" " + theater_location + " -"); // Check for an existing, open theater at the location. Theater theater = Theaters.Theater (theater_location); if (theater == null || ! theater.Opened ()) { // Defer the load for this Theater until it opens. if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" The Theater is not open; deferred profile load ..."); Open_Theater (theater_location); } else Load_Theater_Profile (theater_location); // Remove all definitions for the location. int index = theater_definitions.size (); while (--index >= 0) if (theater_location.equals (theater_definitions.get (index) .get (Profile.THEATER_LOCATION_INDEX))) theater_definitions.remove (index); } if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println ("<<< Kapellmeister.Load_Profile"); } private void Load_Theater_Profile ( String theater_location ) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (">>> Kapellmeister.Load_Theater_Profile: " + theater_location); if (Current_Profile == null || theater_location == null || theater_location.length () == 0) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println ("<<< Kapellmeister.Load_Theater_Profile"); return; } Vector theater_definition; int entry = -1; while ((entry = Current_Profile.Next_Theater_Index (theater_location, entry)) >= 0) { // Remove the theater definition so it will not be reprocessed. theater_definition = Current_Profile.Remove_Theater_Definition (entry--); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" theater_definition - " + theater_definition); Theater theater = Theaters.Theater (theater_location); if (theater != null && theater_definition.size () > Profile.CONDUCTOR_NAME_INDEX) { String conductor_name = (String)theater_definition.get (Profile.CONDUCTOR_NAME_INDEX); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" conductor_name - " + conductor_name); if (conductor_name == null) continue; Conductor_Definition conductor_definition = Current_Profile.Conductor_Definition (conductor_name); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" conductor_definition -" + NL + conductor_definition); if (conductor_definition == null) { // Use a generic Conductor_Definition. conductor_definition = new Conductor_Definition (conductor_name); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" new Conductor_Definition -" + NL + conductor_definition); } Integer conductor_count = null; if (theater_definition.size () > Profile.CONDUCTOR_COUNT_INDEX) conductor_count = (Integer)theater_definition.get (Profile.CONDUCTOR_COUNT_INDEX); if (conductor_count == null) conductor_count = new Integer (0); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Count: " + conductor_count); int total = Conductors.Count (theater_location, conductor_definition); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Current: " + total); total -= conductor_count; if (total < 0) { try { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Start: " + -total + NL +" Start_Conductor_Message -" + NL + conductor_definition.Start_Conductor_Message (Wait_to_Start_Checkbox.isSelected (), -total)); New_Conductor (theater, conductor_definition.Start_Conductor_Message (Wait_to_Start_Checkbox.isSelected (), -total)); } catch (Exception exception) { String report = "The Conductor matrix Profile contains an invalid " + "Conductor definition -" + NL + conductor_definition + NL + exception.getMessage (); Report ("!!! " + report); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" " + report); Dialog_Box.Notice (report, Conductor_Table); } } else if (total > 0 && Dialog_Box.Confirm ("Quit or Stop " + total + ' ' + conductor_definition.Name () + " Conductor" + ((total == 1) ? "" : "s") + NL +"on the " + theater_location + " Theater?", Conductor_Table)) { // Stop the excess Conductors. if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Stop: " + total); // Search for waiting Conductors that can be quit. Manager manager; int index = -1; while (total > 0 && (index = Conductors.Next_Index (theater_location, index)) >= 0) { if (conductor_definition.Matches (Conductors.Identity (index))) { if (Conductors.Processing_State (index) < 0) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Quiting -" + NL + Conductors.Identity (index)); if ((manager = Conductors.Manager (index)) != null) manager.Quit_Conductor (); else Conductors.Management (index).Quit (); --index; --total; } } } // Stop any remaining excess. if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Stop remaining: " + total); index = -1; while (total > 0 && (index = Conductors.Next_Index (theater_location, index)) >= 0) { if (conductor_definition.Matches (Conductors.Identity (index))) { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Stopping -" + NL + Conductors.Identity (index)); Conductors.Management (index).Stop (); --total; } } } } } if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println ("<<< Kapellmeister.Load_Theater_Profile"); } private void Save_Profile ( boolean inquire ) { if (Theaters.Total_Opened () == 0) { Dialog_Box.Notice ("There are no open theaters" + NL +"from which to create a profile that can be saved."); return; } File file = null; if (Profile_Pathname == null || inquire) { if (File_Chooser.showSaveDialog (this) != JFileChooser.APPROVE_OPTION) return; file = File_Chooser.getSelectedFile (); } else file = new File (Profile_Pathname); try {CWD (file);} catch (FileNotFoundException exception) { Dialog_Box.Notice ("The pathname to " + file.getPath () + '\n' +"does not exist.", this); return; } if (file.exists ()) { if (file.isDirectory ()) { Dialog_Box.Notice ("Can't replace directory " + file.getAbsolutePath (), this); return; } if (! Dialog_Box.Confirm ("Replace " + file.getAbsolutePath () + '?', this)) return; } try { Profile profile; if (Conductors.getRowCount () != 0) profile = Conductors.Profile (); else { profile = new Profile (); int index = -1, size = Theaters.getSize (); while (++index < size) if (Theaters.Opened (index)) profile.Add_Theater_Definition (Theaters.Location (index)); } profile.Write (file.getAbsolutePath ()); Profile_Pathname = file.getAbsolutePath (); } catch (Exception exception) { TOOLKIT.beep (); new Error_Report ("Profile Save Failed", "There was a problem while saving the Conductors Profile to -" + NL + file.getAbsolutePath (), exception, this); } } /** Sets the current working directory for finding Profile files.

@param pathname A host filesystem pathname. If null, the "user.dir" System property will be used. @return This Kapellmeister. @throws FileNotFoundException If neither the file nor the file's parent refers to an existing directory. @see #CWD(File) */ public Kapellmeister CWD ( String pathname ) throws FileNotFoundException { if (pathname == null) pathname = System.getProperty ("user.dir"); return CWD (new File (pathname)); } /** Sets the current working directory for finding Profile files.

@param file A File to which to set the CWD. If null, the "user.dir" System property will be used. If the file does not refer to an existing directory, the file's parent will be used. @return This Kapellmeister. @throws FileNotFoundException If neither the file nor the file's parent refers to an existing directory. */ public Kapellmeister CWD ( File file ) throws FileNotFoundException { if (file == null) file = new File (System.getProperty ("user.dir")); if (! file.isAbsolute ()) file = file.getAbsoluteFile (); if (! file.isDirectory ()) // Can only set directories. file = file.getParentFile (); if (file.exists ()) File_Chooser.setCurrentDirectory (file); else throw new FileNotFoundException (ID + '\n' +"Can't set working directory to" + NL + file.getAbsolutePath () + NL +"No such directory."); return this; } /*------------------------------------------------------------------------------ Theaters */ private Theater Open_Theater ( String location ) { if (location == null || location.length () == 0) return null; if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Open_Theater: " + location); // Check for an existing, open theater at the location. Theater theater = Theaters.Theater (location); if (theater == null || ! theater.Opened ()) { // Open the theater. String hostname = Theater.Host (location); if (hostname == null || hostname.length () == 0) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" No hostname" + NL +"<<< Kapellmeister.Open_Theater"); return null; } hostname = Host.IP_Address (hostname); if (hostname == null) { Dialog_Box.Notice ("Unknown theater host for location" + NL +'"' + location + "\".", Theater_List); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Unknown theater host for location" + NL +"<<< Kapellmeister.Open_Theater"); return null; } int port = Theater.Port (location); if (port < 0) { Dialog_Box.Error ("Invalid theater host port number for location" + NL +'"' + location + "\".", Theater_List); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Invalid theater host port number for location" + NL +"<<< Kapellmeister.Open_Theater"); return null; } location = Theater.Full_Location (location); String theater_key; if ((theater_key = Theater_Keys.get (location)) == null && (theater_key = Skeleton_Key) == null && (theater_key = Theater_Key (location)) == null) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Password entry cancelled" + NL +"<<< Kapellmeister.Open_Theater"); return null; } setCursor (WAIT_CURSOR); Exception thrown = null; try { if (theater == null) { // New theater. if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Opening a new Theater on host " + hostname + " port " + port + '.'); Report ("==> Opening a new Theater on host " + hostname + " port " + port + '.'); theater = new Theater (hostname, port, Keyed_Identity (theater_key)); } else { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Reopening a Theater -" + NL + theater); Report ("==> Reopening a Theater -" + NL + theater); // Re-open the existing theater. theater.Open (hostname, port, Keyed_Identity (theater_key)); } } catch (IOException exception) { if (theater == null && Theater_Keys.get (location) == null && exception instanceof Theater_Protocol_Exception && ((Theater_Protocol_Exception)exception).Reason () == Theater_Protocol_Exception.UNAUTHENTICATED) { // The skeleton key did not open the new theater. if ((theater_key = Theater_Key (location)) == null) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Password entry cancelled" + NL +"<<< Kapellmeister.Open_Theater"); return null; } try {theater = new Theater (hostname, port, Keyed_Identity (theater_key));} catch (IOException except) {thrown = except;} } else thrown = exception; } setCursor (DEFAULT_CURSOR); if (thrown != null) { String report = "Unable to open the Theater" + ((theater == null) ? (" at " + location + " (" + hostname + ':' + port + ").") : ("." + NL + theater)); Report ("!!! " + report + NL + thrown); new Error_Report ("Theater Open Failure", report, thrown, this); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Theater open failed." + NL + report + NL +"<<< Kapellmeister.Open_Theater"); return null; } Report ("==> Opened Theater -" + NL + theater + NL +" with Stage Manager -" + NL + theater.Stage_Manager_Identity ()); if (Theater_Keys.get (location) == null) // Cache the theater key. Theater_Keys.put (location, theater_key); if (Skeleton_Key == null) Skeleton_Key = theater_key; // Add the theater to the list BEFORE listening for messages. if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Add theater to List -" + NL + theater); Theaters.Add (theater); Theater_Selected (theater.Location ()); // Begin listening for Messages. if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Start listening for messages"); theater.Employer (this); theater.Listen_for_Messages (); // Request reports of Messengers connected to the Stage_Manager. if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Requesting Messenger connection reports"); try {theater.Send_Message (Message.Action (Theater.START_MESSENGER_REPORTING_ACTION));} catch (IOException exception) { // A Done message for the theater will be delivered. String report = "There was a problem sending the Theater Stage Manager the " + Theater.START_MESSENGER_REPORTING_ACTION + " request:" + NL + NL + theater; Report ("!!! " + report + NL + exception); new Error_Report ("Theater Open Failure", report, exception, this); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Theater open failed." + NL + report + NL + exception + NL +"<<< Kapellmeister.Open_Theater"); return null; } } if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Theater -" + NL + theater + NL +"<<< Kapellmeister.Open_Theater"); return theater; } private void Open_Theaters ( Vector theater_locations ) { if (theater_locations == null || theater_locations.size () == 0) return; int index = -1, size = theater_locations.size (); while (++index < size) Open_Theater (theater_locations.get (index)); } private void Open_Theater () { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Open_Theater"); String location = (String)Theater_List.getSelectedItem (); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Theater location - " + location); if (location == null || location.length () == 0) { Dialog_Box.Notice ("A theater location is required.", Theater_List); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" No location" + NL +"<<< Kapellmeister.Open_Theater"); return; } // Check for an existing, opened Theater. Theater theater = Theaters.Theater (location); if (theater == null || ! theater.Opened ()) theater = Open_Theater (location); else Close_Theater (theater, null); Theater_is_Open ((theater == null) ? false : theater.Opened ()); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println ("<<< Kapellmeister.Open_Theater"); } private void Close_Theater ( Theater theater, Message message ) { if (theater == null) return; if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Close_Theater:" + NL + theater); String location = Theaters.Location (theater); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Theater location - " + location); if (location != null) { Report ("==> Closing the theater located at " + location); if (location.equals (Theater.Full_Location ((String)Theater_List.getSelectedItem ()))) Theater_is_Open (false); if (message != null) { String report = Message.Unescape (message.Get (Theater.EXCEPTION_PARAMETER_NAME)); if (report != null) { report = "The theater at " + location + " unexpectedly closed." + NL + NL + report + NL + theater; Report ("!!! " + report); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" " + report); Dialog_Box.Notice (report, Theater_List); } } if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Suspending Messenger connection reports"); try {theater.Send_Message (Message.Action (Theater.STOP_MESSENGER_REPORTING_ACTION));} catch (IOException exception) {/* Remove all identies for the theater anyway */} // Remove all Conductors at the theater location. Conductors.Remove (location); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Restart Messenger connection reports"); try {theater.Send_Message (Message.Action (Theater.START_MESSENGER_REPORTING_ACTION));} catch (IOException exception) {} } // Make sure the theater is closed. Theaters.Close (theater); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println ("<<< Kapellmeister.Close_Theater"); } /** Theaters_List ItemList ItemEvent handler. */ private void Theater_Selected () { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Theater_Selected"); String location = Theater.Full_Location ((String)Theater_List.getSelectedItem ()); if (location == null || location.length () == 0) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" No location selected"); location = Theater.Full_Location (Theaters.Previous_Location ()); if (location != null && location.length () != 0) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Restoring previous location: " + location); Theater_Selected (location); TOOLKIT.beep (); } } else { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Location selected: " + location); Theater theater = Theaters.Theater (location); if (theater != null && theater.Opened ()) Select_Conductors_at (location); else theater = Open_Theater (location); Theater_is_Open ((theater == null) ? false : theater.Opened ()); } if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println ("<<< Kapellmeister.Theater_Selected"); } /** Select the Theater location to be selected in the Theaters_List.

@param location The Theater location String in standard format as it is expected to appear in the Theaters_List. If null or the location is already selected nothing is done. */ private void Theater_Selected ( String location, boolean update_conductor_selections ) { location = Theater.Full_Location (location); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Theater_Selected: " + location); if (location != null && ! location.equals (Theater.Full_Location ((String)Theater_List.getSelectedItem ()))) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (" Selecting"); // Remove listener to prevent another selection event. Theater_List.removeItemListener (Theater_List_ItemListener); Theater_List.setSelectedItem (location); // Restore listener. Theater_List.addItemListener (Theater_List_ItemListener); Theater_is_Open (Theaters.Opened (location)); if (update_conductor_selections) Select_Conductors_at (location); } if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println ("<<< Kapellmeister.Theater_Selected"); } private void Theater_Selected ( String location ) {Theater_Selected (location, true);} private void Theater_is_Open ( boolean opened ) { if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println (">>> Kapellmeister.Theater_is_Open: " + opened); if (opened) { if (Close_Theater_Icon == null) { Open_Theater_Button.setText ("Close"); Open_Theater_Button.setBackground (Colors.CLOSED_THEATER); } else Open_Theater_Button.setIcon (Close_Theater_Icon); Open_Theater_Button.setToolTipText ("Press to close the theater"); } else { if (Open_Theater_Icon == null) { Open_Theater_Button.setText ("Open"); Open_Theater_Button.setBackground (Colors.OPENED_THEATER); } else Open_Theater_Button.setIcon (Open_Theater_Icon); Open_Theater_Button.setToolTipText ("Press to open the theater"); } Open_Theater_Button.setEnabled (true); if (Theater_List != null) Theater_List.getEditor ().getEditorComponent ().setBackground (opened ? Colors.Selected_Color (true, Colors.OPENED_THEATER) : Colors.Selected_Color (true, Colors.CLOSED_THEATER)); if (New_Conductor_Action != null && Conductor_Table != null) { if (! opened && Conductor_Table.getSelectedRowCount () == 0) New_Conductor_Action.setEnabled (false); else New_Conductor_Action.setEnabled (true); } if (Theater_Status_Action != null) Theater_Status_Action.setEnabled (opened); if ((DEBUG & DEBUG_THEATER_CONNECT) != 0) System.out.println ("<<< Kapellmeister.Theater_is_Open"); } private void Theater_Status_Report () { String location = (String)Theater_List.getSelectedItem (); if (location == null || location.length () == 0) { Dialog_Box.Notice ("A theater location must be selected.", Monitor_Window); return; } // Check for an existing, opened Theater. Theater theater = Theaters.Theater (location); if (theater == null || ! theater.Opened ()) { Dialog_Box.Notice ("The " + location + " theater is not open.", Monitor_Window); return; } Theater_Status_Report (new Message () .Set (location, "Status requested ...") .Name (location)); Theater_Status_Report (theater); } private void Theater_Status_Report ( Theater theater ) { if (theater == null) return; String location = theater.Location (); Theater_Status_Report (new Message () .Set (location, "Status requested ...") .Name (location)); try {theater.Send_Message (Message.Action (Theater.STATUS_REPORT_ACTION));} catch (IOException exception) { Theater_Status_Report (new Message () .Set (location, "Unable to obtain the status") .Name (location)); new Error_Report ("Theater Status Report Failure", "Unable to send the " + Theater.STATUS_REPORT_ACTION + " request.", exception, Theater_Status_Report_View); } } private void Theater_Status_Report ( Messenger messenger, Message message ) { if (messenger == null || message == null) return; message.Remove (Theater.ACTION_PARAMETER_NAME); message.Name (Theaters.Location (messenger)); Theater_Status_Report (message); } private void Theater_Status_Report ( Parameter report ) { if (report == null) return; String location = report.Name (); report.Name (Parser.CONTAINER_NAME); if (Theater_Status_Report_View == null) { Theater_Status_Report_View = new Parameter_View (location + " Theater Status", report); Theater_Status_Report_View.setDefaultCloseOperation (JFrame.HIDE_ON_CLOSE); View_Locator locator = new View_Locator () .Offsets (0, 0) .Vertical (View_Locator.TOP | View_Locator.INWARD) .Horizontal (View_Locator.RIGHT | View_Locator.OUTWARD); locator.Relocate (Theater_Status_Report_View, Monitor_Window); } else { Theater_Status_Report_View.setTitle (location + " Theater Status"); Theater_Status_Report_View.Parameter_Pane ().Parameter (report); } Theater_Status_Report_View.setVisible (true); Theater_Status_Report_View.toFront (); } /** Get the Stage_Manager key for a Theater location.

If a key for the location can be obtained from the Theater_Keys cache it is returned. If no key is obtained a Password dialog is used to obtain a key string. An empty key string will only be accepted if {@link Stage_Manager#UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true. Otherwise a Notice is displayed at the Password dialog repeated.

@param location A Theater location string. If null, null is returned. @return A Theater Stage_Manager key (password) string. This will be null if a key could be obtained from the Theater_Keys cache and the user canceled the Password dialog. It will only be the emtpy string if {@link Stage_Manager#UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true. */ private String Theater_Key ( String location ) { if (location == null) return null; String key = Theater_Keys.get (location); while (key == null) { key = Password_Dialog.Get_Password ("Kapellmeister Theater Opening", location + " Stage_Manager Password", this); /* Note: The Password will only be null if the user selected Canel. It will be empty if Accept was selected without entering a password. */ if (key != null && key.length () == 0 && ! Stage_Manager.UNAUTHENTICATED_CONNECTIONS_ALLOWED) { Dialog_Box.Notice ("A Stage_Manager password is required.", this); key = null; } else break; } return key; } private Message Keyed_Identity ( String theater_key ) { Message identity = Listener_Identity (); if (theater_key != null && theater_key.length () != 0) identity.Set (THEATER_KEY_PARAMETER_NAME, theater_key); return identity; } /*------------------------------------------------------------------------------ Conductors */ private void New_Conductor () { if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (">>> Kapellmeister.New_Conductor"); String location = null; // Check for a selected Conductor. int row = Conductor_Table.getSelectedRow (); if (row >= 0) { // Clone the selected Conductor on the same Theater. Message identity = Conductors.Identity (Conductor_Table.convertRowIndexToModel (row)); if (identity != null) { New_Conductor_Dialog .Name (Conductors.Conductor_Name (identity)) .Pipeline (identity.Get (PIPELINE_PARAMETER_NAME)) .Catalog (identity.Get (CATALOG_PARAMETER_NAME)) .Configuration (identity.Get (CONFIGURATION_SOURCE_PARAMETER_NAME)) .Server (identity.Get (DATABASE_SERVER_PARAMETER_NAME)); location = Conductors.Theater_Location (identity); } } if (location == null) // Use the selected Theater. location = Theater.Full_Location ((String)Theater_List.getSelectedItem ()); if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (" Theater location: " + location); Theater theater = Theaters.Theater (location); if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (" Theater -" + NL + theater); if (theater == null || ! theater.Opened ()) { Dialog_Box.Notice ("An opened theater location must be selected.", Theater_List); if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (" No selected theater" + NL +"<<< Kapellmeister.New_Conductor"); return; } Theater_Selected (location); New_Conductor_Dialog.Title ("New Conductor at " + location); New_Conductor_Dialog.setLocation (Conductor_Table.getLocationOnScreen ()); New_Conductor_Dialog.setVisible (true); New_Conductor (theater, New_Conductor_Dialog.Start_Conductor_Message ()); if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println ("<<< Kapellmeister.New_Conductor"); } private void New_Conductor ( Theater theater, Message conductor_definition ) { if (theater == null || conductor_definition == null) return; if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (">>> Kapellmeister.New_Conductor:" + NL +" Theater -" + NL + theater + NL +" Conductor definition -" + NL + conductor_definition); Report ("==> Starting new Conductor(s):" + NL +"Theater -" + theater + NL +"Conductor definition -" + NL + conductor_definition); try {theater.Send_Message (conductor_definition);} catch (IOException exception) { String report = "There was a problem sending the new Conductor message:" + NL + NL + exception + NL +"Message -" + NL + conductor_definition + NL +"Theater -" + NL + theater + NL + NL + ID; Report ("!!! " + report); if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println (" " + report); Dialog_Box.Error (report, Conductor_Table); // A Done message for the theater will be delivered. } if ((DEBUG & DEBUG_NEW_CONDUCTOR) != 0) System.out.println ("<<< Kapellmeister.New_Conductor"); } private void New_Conductor_Reply ( Message message ) { if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (">-< Kapellmeister.New_Conductor_Reply:" + NL + message); String explanation = Message.Unescape (message.Get (Theater.EXPLANATION_PARAMETER_NAME)); if (explanation != null) { String string = message.Get (Stage_Manager.COMMAND_PARAMETER_NAME); if (string != null && explanation.indexOf (string) < 0) explanation += NL + NL + "Command line: " + string; new Error_Report ("Conductor startup failure", explanation, Message.Unescape (message.Get (Theater.EXCEPTION_PARAMETER_NAME)), this); } } /** Get the Remote_Theater Management for a Conductor.

If the Conductors table contains a {@link Conductor_Table_Model#Management(Message) Remote_Theater Management for the identity} which is {@link Remote_Theater#Opened() open}, that is returned. Otherwise an attempt is made to obtain it.

Remote_Theater Management is obtained by first getting the {@link Conductor_Table_Model#Theater_Location(Message) theater location from the identity}. This is used to obtain the {@link Open_Theater(String) Theater for the location}. The Theater, along with the Conductor identity and {@link #Listener_Identity() Kapellmeister identity */ private Remote_Theater Management ( Message identity ) { if (identity == null) return null; if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (">>> Kapellmeister.Management:" + NL + identity); Remote_Theater management = Conductors.Management (identity); if (management != null && ! management.Opened ()) management = null; if (management == null) { String location = Conductors.Theater_Location (identity); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Conductor theater location: " + location); if (location != null) { Theater theater = Open_Theater (location); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Theater: " + theater); if (theater != null) { try {management = Conductors.Open_Management (identity, theater, Keyed_Identity (Theater_Key (location)));} catch (IOException exception) { String report = "Management for a Conductor located at " + location + NL +"could not be obtained." + NL + NL +"Conductor identity -" + NL + identity + NL +"Theater -" + NL + theater; Report ("!!! " + report + NL + exception); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" " + report + NL + exception); new Error_Report ("Conductor Management Failure", report, exception, this); } } } else { String report = "The " + Conductors.Identification (identity) + " Conductor does not have a theater location!" + NL + NL +"Conductor identity -" + NL + identity + NL + NL + ID; Report ("!!! " + report); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" " + report); new Error_Report ("Conductor Management Failure", report, this); } } if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Management -" + NL + management + NL +"<<< Management"); return management; } private void Open_Manager () { if (Conductor_Table.getSelectedRowCount () > 0) { int[] rows = Conductor_Table.getSelectedRows (); for (int count = 0; count < rows.length; count++) Open_Manager (Conductors.Identity (Conductor_Table.convertRowIndexToModel (rows[count]))); } } private Manager Open_Manager ( Message identity ) { if (identity == null) return null; if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (">>> Kapellmeister.Open_Manager:" + NL + identity); int index = Conductors.Index (identity); if (index < 0) { Dialog_Box.Notice ("The " + Conductors.Identification (identity) + " Conductor can not be found!" + NL +"Conductor identity -" + NL + identity + NL + NL + ID, Conductor_Table); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" No Conductor identity in the data model!" + NL +"<<< Kapellmeister.Open_Manager"); return null; } Manager manager = Conductors.Manager (index); if (manager == null) { if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Opening new Manager"); Remote_Theater remote_theater = Management (identity); if (remote_theater != null) { Report ("==> Starting a new Conductor Manager." + NL +"Conductor identity -" + NL + identity); try { manager = new Manager (remote_theater, Kapellmeister_Configuration); manager.addWindowListener (new WindowAdapter () {public void windowClosed (WindowEvent event) {Close_Manager ((Manager)event.getWindow ());}}); Conductors.Manager (index, manager); } catch (Exception exception) { String report = "A Manager could not be constructed for the Conductor -"+ NL + NL + identity; Report ("!!! " + report + NL + exception); new Error_Report ("New Manager Failure", report, exception, this); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Manager open failed" + NL + report + NL + exception); manager = null; } } } else { manager.toFront (); if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println (" Manager already open."); } if ((DEBUG & DEBUG_CONDUCTOR_CONNECT) != 0) System.out.println ("<<< Open_Manager"); return manager; } private void Close_Manager ( Manager manager ) {Conductors.Close (manager);} private void Close_Manager () { if (Conductor_Table.getSelectedRowCount () > 0) { int[] rows = Conductor_Table.getSelectedRows (); /* An array of Managers are obtained rather the using the data model row indices because the model, and thus the validity of row indices, may change as the selected rows are processed. */ Manager[] managers = new Manager[rows.length]; int index = rows.length; while (--index >= 0) managers[index] = Conductors.Manager (Conductor_Table.convertRowIndexToModel (rows[index])); while (++index < managers.length) if (managers[index] != null) managers[index].Close (); } } private void Start_Conductor () { if (Conductor_Table.getSelectedRowCount () > 0) { int[] rows = Conductor_Table.getSelectedRows (); /* An array of Managements are obtained rather the using the data model row indices because the model, and thus the validity of row indices, may change as the selected rows are processed. */ Management[] managements = new Management[rows.length]; int index = rows.length; while (--index >= 0) managements[index] = Conductors.Management (Conductor_Table.convertRowIndexToModel (rows[index])); while (++index < managements.length) { if (managements[index] != null) { try {managements[index].Start ();} catch (Remote_Management_Exception exception) { Dialog_Box.Error ("Conductor management protocol failure" + NL + NL + Message.Unescape (exception.getMessage ()), Conductor_Table); } } } } } private void Reset_Conductor () { if (Conductor_Table.getSelectedRowCount () > 0) { int[] rows = Conductor_Table.getSelectedRows (); /* An array of data model identity Message objects are obtained rather the using the data model row indices for each operation because the model, and thus the validity of row indices, is likely to change as the selected rows are processed. */ Message[] identities = new Message[rows.length]; int index = rows.length; while (--index >= 0) identities[index] = Conductors.Identity (Conductor_Table.convertRowIndexToModel (rows[index])); Management management; while (++index < identities.length) { if (identities[index] == null || (management = Conductors.Management (identities[index])) == null) continue; if (Conductors.Processing_State (identities[index]) == Conductor_Table.HALTED_STATE) { try {management.Reset_Sequential_Failures ();} catch (Remote_Management_Exception exception) { Dialog_Box.Error ("Conductor management protocol failure" + NL + NL + Message.Unescape (exception.getMessage ()), Conductor_Table); } } } } } private void Stop_Conductor () { if (Conductor_Table.getSelectedRowCount () > 0) { int[] rows = Conductor_Table.getSelectedRows (); /* An array of Managements are obtained rather the using the data model row indices because the model, and thus the validity of row indices, may change as the selected rows are processed. */ Management[] managements = new Management[rows.length]; int index = rows.length; while (--index >= 0) managements[index] = Conductors.Management (Conductor_Table.convertRowIndexToModel (rows[index])); while (++index < managements.length) { if (managements[index] != null) managements[index].Stop (); } } } private void Quit_Conductor () { if (Conductor_Table.getSelectedRowCount () > 0) { boolean check = Confirm_Conductor_Abort_Checkbox.isSelected (); int[] rows = Conductor_Table.getSelectedRows (); /* An array of data model identity Message objects are obtained rather the using the data model row indices for each operation because the model, and thus the validity of row indices, is likely to change as the selected rows are processed. */ Message[] identities = new Message[rows.length]; int index = rows.length; while (--index >= 0) identities[index] = Conductors.Identity (Conductor_Table.convertRowIndexToModel (rows[index])); Manager manager; Management management; int processing_state; while (++index < identities.length) { if (identities[index] == null) continue; if ((manager = Conductors.Manager (identities[index])) != null) manager.Quit_Conductor (); else if ((management = Conductors.Management (identities[index])) != null) { if (check) { processing_state = Conductors.Processing_State (identities[index]); if (processing_state == Conductor_Table.RUNNING_STATE || processing_state == Conductor_Table.POLLING_STATE) { if (! Dialog_Box.Confirm ("Abort the active " + Conductors.Conductor_Name (identities[index]) + " Conductor" + NL +"at the " + Conductors.Theater_Location (identities[index]) + " Theater?", Conductor_Table)) continue; } } management.Quit (); } } } } private void Select_Conductors_at ( String location ) { if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">>> Kapellmeister.Select_Conductors_at: " + location); ListSelectionModel selector = Conductor_Table.getSelectionModel (); int first_row = -1, last_row = -1, row, index = -1; while((row = Conductors.Next_Index (location, index)) >= 0) { if (index < 0) { // Remove the selection listener to prevent spurious updates. selector.removeListSelectionListener (Conductor_Table_Selection_Listener); // Deselect all current selections. Conductor_Table.clearSelection (); } index = row; row = Conductor_Table.convertRowIndexToView (row); if (first_row < 0) // Initialize the row range indices. first_row = last_row = row; else if (++last_row != row) { // Start of new non-contiguous interval. Add previous interval. selector.addSelectionInterval (first_row, --last_row); first_row = last_row = row; } } if (first_row >= 0) { // Final interval. selector.addSelectionInterval (first_row, last_row); selector.addListSelectionListener (Conductor_Table_Selection_Listener); Refresh_Conductor_Ops_Menus (); } if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println ("<<< Kapellmeister.Select_Conductors_at"); } private void Select_Conductors_for ( String name ) { if (name == null || name.length () == 0) return; int size = Conductors.getRowCount (); if (size == 0) return; if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">>> Kapellmeister.Select_Conductors_for: " + name); ListSelectionModel selector = Conductor_Table.getSelectionModel (); // Remove the selection listener to prevent spurious updates. selector.removeListSelectionListener (Conductor_Table_Selection_Listener); // Deselect all current selections. Conductor_Table.clearSelection (); int first_row = -1, last_row = -1, row = -1; while(++row < size) { if (! Conductors.Conductor_Name (row).equals (name)) continue; row = Conductor_Table.convertRowIndexToView (row); if (first_row < 0) // Initialize the row range indices. first_row = last_row = row; else if (++last_row != row) { // Start of new non-contiguous interval. Add previous interval. selector.addSelectionInterval (first_row, --last_row); first_row = last_row = row; } } if (first_row >= 0) { // Final interval. selector.addSelectionInterval (first_row, last_row); selector.addListSelectionListener (Conductor_Table_Selection_Listener); Refresh_Conductor_Ops_Menus (); } if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println ("<<< Kapellmeister.Select_Conductors_for"); } private void Refresh_Conductor_Ops_Menus () { if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">>> Kapellmeister.Refresh_Conductor_Ops_Menus"); if (Conductor_Table.getSelectedRowCount () == 0) { // Nothing selected. New_Conductor_Action.setEnabled (Theaters.Opened ((String)Theater_List.getSelectedItem ())); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" New: " + Theaters.Opened ((String)Theater_List.getSelectedItem ())); Open_Conductor_Action.setEnabled (false); Close_Conductor_Action.setEnabled (false); Start_Conductor_Action.setEnabled (false); Reset_Conductor_Action.setEnabled (false); Stop_Conductor_Action.setEnabled (false); Quit_Conductor_Action.setEnabled (false); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Open: false" + NL +" Close: false" + NL +" Start: false" + NL +" Stop: false" + NL +" Quit: false" + NL +"<<< Kapellmeister.Conductor_Selected"); return; } int[] rows = Conductor_Table.getSelectedRows (); int index = rows.length; if ((DEBUG & DEBUG_SELECTION) != 0) System.out.print (" " + index + " table rows selected:"); while (--index >= 0) { if ((DEBUG & DEBUG_SELECTION) != 0) System.out.print (" " + rows[index]); rows[index] = Conductor_Table.convertRowIndexToModel (rows[index]); } if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (); // New New_Conductor_Action.setEnabled (true); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" New: true"); // Open while (++index < rows.length) if (Conductors.Manager (rows[index]) == null) break; Open_Conductor_Action.setEnabled (index < rows.length); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Open: " + (index < rows.length)); // Close index = -1; while (++index < rows.length) if (Conductors.Manager (rows[index]) != null) break; Close_Conductor_Action.setEnabled (index < rows.length); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Close: " + (index < rows.length)); // Start index = -1; while (++index < rows.length) if (Conductors.Processing_State (rows[index]) <= 0) break; Start_Conductor_Action.setEnabled (index < rows.length); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Start: " + (index < rows.length)); // Reset index = -1; while (++index < rows.length) if (Conductors.Processing_State (rows[index]) == Conductor_Table.HALTED_STATE) break; Reset_Conductor_Action.setEnabled (index < rows.length); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Reset: " + (index < rows.length)); // Stop index = -1; while (++index < rows.length) if (Conductors.Processing_State (rows[index]) > 0) break; Stop_Conductor_Action.setEnabled (index < rows.length); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Stop: " + (index < rows.length)); // Quit Quit_Conductor_Action.setEnabled (true); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (" Quit: true"); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println ("<<< Kapellmeister.Refresh_Conductor_Ops_Menus"); } private Message Uniquely_Named ( Message identity ) { int index = -1, size = Conductors.getRowCount (); return identity; } private void Matrix_Selected () { } //------------------------------------------------------------------------------ private void Exit ( int status ) { setVisible (false); Conductors.Close_All (); Theaters.Close_All (); //!!! State saving operations. System.exit (status); } /*============================================================================== Messages */ // Message_Delivered_Listener /** Get the idenity of this Messenger client.

@return A copy of the {@link #KAPELLMEISTER_IDENTITY} Message. */ public Message Listener_Identity () { try {return new Message (KAPELLMEISTER_IDENTITY);} catch (PVL_Exception exception) { // This can only happen if the Identity Message has been corrupted. Dialog_Box.Error ("The required Kapellmeister Identity has become corrupted -" + NL + KAPELLMEISTER_IDENTITY + NL + exception + NL + exception.getMessage () + NL + NL + "Unable to continue.", this); Exit (EXIT_UNRECOVERABLE_ERROR); } return null; } private Vector Message_Event_Queue = new Vector (); // Message_Delivered_Listener /** Take delivery of a Message and queue it for disposition.

N.B.: This method is public as a side-effect of implementing the Message_Delivered_Listener interface.

The Message_Delivered_Event is added to a queue and the message disposition method is scheduled for execution on the Swing event thread where the Message_Delivered_Event will be retrieved from the queue and examined for appropriate disposition.

@param event The Message_Delivered_Event that arrived from a Messenger. @see Message_Delivered_Event */ public void Message_Delivered ( Message_Delivered_Event event ) { synchronized (Message_Event_Queue) {Message_Event_Queue.add (event);} SwingUtilities.invokeLater (new Runnable () {public void run () {Message_Disposition ();}}); } private void Message_Disposition () { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Kapellmeister.Message_Disposition" + NL +" Message_Event_Queue size: " + Message_Event_Queue.size ()); // Pull the next Message_Delivered_Event from the front of the queue. Message_Delivered_Event event; synchronized (Message_Event_Queue) { /* >>> WARNING <<< No check is being done for an empty queue because Message_Disposition is run from the event queue after a Message_Delivered enqueues the delivered event, and only one event is removed from the queue each time Message_Disposition is run. If this one-to-one relationship between Message_Delivered and Message_Disposition is ever changed an empty queue check must be added. */ event = Message_Event_Queue.remove (0); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Kapellmeister.Message_Delivered: " + event.Message.Action () + NL +" From -" + NL + (Messenger)event.getSource () + NL + ((Messenger)event.getSource ()).Identity () + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); Report ("<<< Message delivered:" + NL + event.Message.Routing () + NL + event.Message); // Message disposition depends on its Action. switch (Theater.Action_Code (event.Message.Action ())) { case Theater.MESSENGERS_REPORT_CODE: Conductor_Identities ((Messenger)event.getSource (), event.Message); break; case Theater.START_CONDUCTOR_CODE: New_Conductor_Reply (event.Message); break; case Theater.IDENTIFY_CODE: Send ((Messenger)event.getSource (), Listener_Identity () .Reply_To (event.Message)); break; case Theater.STATUS_REPORT_CODE: Theater_Status_Report ((Messenger)event.getSource (), event.Message); break; case Theater.DONE_CODE: if ((DEBUG & DEBUG_DONE) != 0) System.out.println ("==> Kapellmeister.Message_Disposition: Done" + NL + (Messenger)event.getSource () + NL + event.Message); Close_Theater (Theaters.Theater ((Messenger)event.getSource ()), event.Message); break; case Theater.UNDELIVERABLE_CODE: case Theater.NACK_CODE: default: Report ("!!! Unexpected Message -" + NL + event.Message); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Kapellmeister.Message_Disposition"); } private void Send ( Messenger messenger, Message message ) { if (messenger == null || message == null) return; if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Kapellmeister.Send:" + NL + messenger + NL + message.Routing () + NL + message); Report (">>> Sending message:" + NL + message.Routing () + NL + message); try {messenger.Send (message);} catch (IOException exception) { if (! (exception instanceof EOFException)) { String report = "A message could not be sent:" + NL + exception + NL +"Message -" + NL + message + NL +"Messenger -" + NL + messenger + NL + NL + ID; Report ("!!! " + report); message.Set (Theater.EXCEPTION_PARAMETER_NAME, exception.toString ()); } Close_Theater (Theaters.Theater (messenger), message); } catch (PVL_Exception exception) { String report = "A message to be sent was found to be corrupted:" + NL + exception + NL + exception.getMessage () + NL +"Message -" + NL + message + NL +"Messenger -" + NL + messenger + NL + NL + ID; Report ("!!! " + report); if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (" " + exception + NL + report); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Kapellmeister.Send"); } /** Update the table Conductors table of identities from the client identities Message received from the Stage_Manager.

A Vector of identity Messages is assembled from the Messenger's Message. The Message is expected to contain zero or more Parameter Groups with the {@link Theater#IDENTITY_ACTION} name. Each such group that contains a {@link Theater#NAME_PARAMETER_NAME} with the {@link #CONDUCTOR_NAME} value and a non-null {@link Theater#ROUTE_TO_PARAMETER_NAME} value will be stored in the identities Vector as a new Message copied from Group. This Message will also have its {@link Message#Route_To(Value) route-to} list set to the Value of the {@link Theater#ROUTE_TO_PARAMETER_NAME}, and the {@link #THEATER_LOCATION_PARAMETER_NAME} set to the {@link Theater_List_Model#Location(Messenger) theater location obtained from the Theaters list. */ private void Conductor_Identities ( Messenger messenger, Message message ) { if (messenger == null || message == null) return; if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (">>> Kapellmeister.Conductor_Identities:" + NL + message); Theater theater = Theaters.Theater (messenger); if (theater == null) { Report ("!!! A " + Theater.MESSENGERS_REPORT_ACTION + " was received from an unknown theater messenger -" + NL + messenger + NL + message); if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (" Unknown theater!" + NL +"<<< Kapellmeister.Conductor_Identities"); return; } String error_report = null; Vector identities = new Vector (); Message identity; Value route_to; Parameter parameter = null; while ((parameter = message.Find (Theater.IDENTITY_ACTION, parameter)) != null) { try { // Copy the identity parameters into a new Message. identity = new Message (parameter); if (! CONDUCTOR_NAME .equals (identity.Get (Theater.NAME_PARAMETER_NAME))) // Ignore anything but Conductors (for now). continue; route_to = identity.Value_of (Theater.ROUTE_TO_PARAMETER_NAME); if (route_to == null) { String report = "The " + Theater.MESSENGERS_REPORT_ACTION + " from the theater at location " + theater.Location () + NL +"contains an identity with no " + Theater.ROUTE_TO_PARAMETER_NAME + " parameter -" + NL + identity; Report ("!!! " + report); if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (report); if (error_report == null) error_report = report; else error_report += NL + report; continue; } identity.Route_To (route_to); identities.add (identity); } catch (PVL_Exception exception) { // Bad identity parameters! String report = "The " + Theater.MESSENGERS_REPORT_ACTION + " from the theater at location " + theater.Location () + NL +"contains an invalid identity -" + NL + parameter.Description () + NL + exception + NL + exception.getMessage (); Report ("!!! " + report); if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (report); if (error_report == null) error_report = report; else error_report += NL + report; } } if (error_report != null) Dialog_Box.Error (error_report, Conductor_Table); // Update the data model, which will update the Conductor_Table. if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (" Replacing the Conductor identities -" + NL + identities); setCursor (WAIT_CURSOR); try { Conductors.Replace ( identities, theater, /* This theater should always have a valid key because the it is in the Theaters list and must be open to be able to receive this report and thus must have a key registered for it. */ Keyed_Identity (Theater_Key (Theaters.Location (theater))) ); } catch (IOException exception) { String report = "Unable to connect to one or more Conductors at the " + theater.Location () + " theater -" + NL + theater; Report ("!!! " + report + NL + NL + exception.getMessage ()); if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (" Kapellmeister.Conductor_Identities: IOException -" + NL + report); new Error_Report ("Conductor Connection Failure", report, exception, this); } setCursor (DEFAULT_CURSOR); if (Current_Profile != null && Current_Profile.Total_Theater_Definitions () != 0) { if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println (" Load_Theater_Profile ..."); Load_Theater_Profile (theater.Location ()); } if ((DEBUG & DEBUG_IDENTITIES) != 0) System.out.println ("<<< Kapellmeister.Conductor_Identities"); } /*============================================================================== Helpers */ /** Loads the application icons used by the GUI. */ private void Load_Icons () { if (Open_Theater_Icon == null) { if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (">>> Kapellmeister.Load_Icons"); Icons.Directory ("Icons", this.getClass ()); if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (" Loading icons from " + Icons.Directory ()); Kapellmeister_Icon = Icons.Load_Icon (Kapellmeister_Icon_Source); Open_Theater_Icon = Icons.Load_Icon (OPEN_THEATER_ICON_NAME); Close_Theater_Icon = Icons.Load_Icon (CLOSE_THEATER_ICON_NAME); if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (" The Icons were " + ((Open_Theater_Icon == null) ? "not " : "") + "loaded" + NL +"<<< Kapellmeister.Load_Icons"); } } /*============================================================================== Application main */ public static void main ( String[] args ) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (ID + NL); Vector sources = null; String configuration_filename = null; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].charAt (1)) { case 'C': // Configuration case 'c': if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') { System.out.println ("Missing configuration filename"); Usage (); } if (configuration_filename != null) { System.out.println ("Multiple configuration files -" + NL + configuration_filename + NL + "and" + NL + args[count]); Usage (); } configuration_filename = args[count]; break; case 'V': // Version case 'v': System.out.println (ID); System.exit (EXIT_SUCCESS); case 'H': // Help case 'h': default: Usage (); } } else { if (sources == null) sources = new Vector (); if (! sources.contains (args[count])) sources.add (args[count]); } } Configuration configuration = null; if (configuration_filename != null) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Loading the Configuration from " + configuration_filename); try {configuration = new Configuration (configuration_filename);} catch (IllegalArgumentException exception) { System.out.println ("Unable to find the configuration file - " + configuration_filename); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (Configuration_Exception exception) { System.out.println ("Could not load the configuration file - " + configuration_filename + NL + exception.getMessage ()); System.exit (EXIT_CONFIG_FILE_PROBLEM); } } // Use the cross-platform LAF. try {UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ());} catch (Exception exception) {/* Just leave the existing LAF. */} if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Constructing the Kapellmeister"); Kapellmeister kapellmeister = null; try {kapellmeister = new Kapellmeister (sources, configuration);} catch (Configuration_Exception exception) { System.out.println ("Could not load the configuration." + NL + exception.getMessage () + NL); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (Exception exception) { System.out.println ("Unexpected exception!" + NL + ID); exception.printStackTrace (System.out); System.exit (EXIT_UNEXPECTED_EXCEPTION); } } public static void Usage () { System.out.println ("Usage: Kapellmeister [ | [...]]" + NL +" Options -" + NL +" -Configuration " + " (default: " + DEFAULT_CONFIGURATION_FILENAME + ')' + NL +" -Help" + NL ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Conductor/Maestro/Error_Report.java0000644000175000017500000002056111742733133021763 0ustar mathieumathieu/* Error_Report PIRL CVS ID: Error_Report.java,v 1.7 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Viewers.Icons; import PIRL.Viewers.View_Locator; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JScrollPane; import javax.swing.JLabel; import javax.swing.JButton; import javax.swing.ImageIcon; import java.awt.Frame; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.*; import java.awt.Toolkit; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Math; /** An Error_Report is a window for reporting error messages.

The window title may be specified or the default "Error Report" may be used.

If the error icon can be loaded it is displayed in the upper-left corner of the window pane.

A short error message is displayed at the top of the window, to the right of the error icon.

A long error report is displayed in a scoll pane below the error message. The error report may be provided as text or as an Exception from which a stack trace report will be obtained.

@author Bradford Castalia, UA/PIRL @version 1.7 */ public class Error_Report extends JFrame { public static final String ID = "PIRL.Conductor.Maestro.Error_Report (1.7 2012/04/16 06:04:11)"; /** The default window title. */ public static final String DEFAULT_TITLE = "Error Report"; /** The filename for the error icon image.

The filename will be sought in the Icons sibling directory relative to the location of this class. */ public static String Error_Icon_Name = "Exclamation.png"; private static ImageIcon Error_Icon = null; // The size of the scroll pane. private static final Dimension SCROLL_PANE_SIZE = new Dimension (250, 125); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs an Error_Report using optional report text.

@param title The String to be used as the title of the window. If null the title will be "Error Report". @param message The message to be displayed at the top of the window. @param report The error report to be displayed in a scroll pane. If null no scroll pane will be displayed. @param owner The parent Frame opver which the window will be centered. If null the window will be centered on the screen. */ public Error_Report ( String title, String message, String report, Frame owner ) { super ((title == null) ? DEFAULT_TITLE : title); GUI (title, message, report, owner); } /** Constructs an Error_Report using an optional Exception report.

@param title The String to be used as the title of the window. If null the title will be "Error Report". @param message The message to be displayed at the top of the window. @param exception The Exception to have its stack trace displayed in a scroll pane. If null no scroll pane will be displayed. @param owner The parent Frame opver which the window will be centered. If null the window will be centered on the screen. */ public Error_Report ( String title, String message, Exception exception, Frame owner ) { super ((title == null) ? DEFAULT_TITLE : title); String report = null; if (exception != null) { StringWriter report_writer = new StringWriter (); exception.printStackTrace (new PrintWriter (report_writer, true)); report = report_writer.toString (); } GUI (title, message, report, owner); } /** Constructs an Error_Report with only a message.

@param title The String to be used as the title of the window. If null the title will be "Error Report". @param message The message to be displayed at the top of the window. @param owner The parent Frame opver which the window will be centered. If null the window will be centered on the screen. */ public Error_Report ( String title, String message, Frame owner ) {this (title, message, (String)null, owner);} private Error_Report () {} /*============================================================================== GUI */ private void GUI ( String title, String message, String report, Frame owner ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Error_Report.GUI: " + title + NL +" Message -" + NL + message + NL +" Report -" + NL + report); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.anchor = GridBagConstraints.NORTHWEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; location.insets = new Insets (10, 10, 10, 10); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Load_Icon..."); Load_Icon (); if (Error_Icon != null) panel.add (new JLabel (Error_Icon), location); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Message JTextArea ..."); JTextArea text = new JTextArea (message); text.setEditable (false); text.setBackground (panel.getBackground ()); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (text, location); if (report != null) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Report JTextArea ..."); text = new JTextArea (report); text.setEditable (false); text.setMargin (new Insets (5, 5, 5, 5)); JScrollPane scroll_pane = new JScrollPane (text); scroll_pane.setPreferredSize (SCROLL_PANE_SIZE); scroll_pane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); scroll_pane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); location.insets = new Insets (0, 5, 0, 5); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panel.add (scroll_pane, location); } if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" JButton ..."); JButton button = new JButton ("Close"); button.setMnemonic ('C'); button.setDefaultCapable (true); getRootPane ().setDefaultButton (button); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {setVisible (false);}}); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; location.insets = new Insets (5, 5, 5, 5); panel.add (button, location); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Add panel to content pane and pack ..."); getContentPane ().add (panel, BorderLayout.CENTER); pack (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" View_Locator ..."); View_Locator locator = new View_Locator () .Vertical (View_Locator.CENTER) .Horizontal (View_Locator.CENTER); locator.Relocate (this, owner); Toolkit.getDefaultToolkit ().beep (); setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE); setVisible (true); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Error_Report.GUI"); } /*============================================================================== Helpers */ /** Loads the icon. */ private void Load_Icon () { if (Error_Icon == null && Error_Icon_Name != null) { Icons.Directory ("Icons", this.getClass ()); Error_Icon = Icons.Load_Icon (Error_Icon_Name); } } } pirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Matrix.java0000644000175000017500000003174711742733133022633 0ustar mathieumathieu/* Conductor_Matrix PIRL CVS ID: Conductor_Matrix.java,v 1.17 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Colors; import org.jdesktop.swingx.JXTable; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.BorderFactory; import javax.swing.border.Border; import javax.swing.border.BevelBorder; import javax.swing.Box; import javax.swing.ListSelectionModel; import javax.swing.SwingConstants; import java.awt.Component; import java.awt.Color; import java.awt.Font; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Dimension; import java.awt.Graphics; import java.awt.FontMetrics; /** A Conductor_Matrix is a table view of a Conductor_Matrix_Model..

@author Bradford Castalia, UA/PIRL @version 1.17 */ public class Conductor_Matrix extends JXTable { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Matrix (1.17 2012/04/16 06:04:11)"; /** Index for arrays of processing state information. */ public static final int RUNNING_INDEX = Conductor_Matrix_Model.RUNNING, POLLING_INDEX = Conductor_Matrix_Model.POLLING, WAITING_INDEX = Conductor_Matrix_Model.WAITING, HALTED_INDEX = Conductor_Matrix_Model.HALTED, TOTAL_INDEX = Conductor_Matrix_Model.TOTAL; public static final Color STATE_COLORS[][] = { {Colors.RUNNING_STATE, Colors.Selected_Color (true, Colors.RUNNING_STATE)}, {Colors.POLLING_STATE, Colors.Selected_Color (true, Colors.POLLING_STATE)}, {Colors.WAITING_STATE, Colors.Selected_Color (true, Colors.WAITING_STATE)}, {Colors.HALTED_STATE, Colors.Selected_Color (true, Colors.HALTED_STATE)}, {Colors.TABLE, Colors.TABLE_SELECTED} }; /** Processing state label annotations. */ public static final String STATE_ANNOTATIONS[] = {"r", "p", "w", "h", "t"}; /** Bit flags for processing state count selection. */ public static final int RUNNING_COUNT = 1 << RUNNING_INDEX, POLLING_COUNT = 1 << POLLING_INDEX, WAITING_COUNT = 1 << WAITING_INDEX, HALTED_COUNT = 1 << HALTED_INDEX, TOTAL_COUNT = 1 << TOTAL_INDEX, ALL_COUNTS = RUNNING_COUNT | POLLING_COUNT | WAITING_COUNT | HALTED_COUNT | TOTAL_COUNT; private int Counts_Shown = 0; private boolean State_Annotate = false; private Font Emphasis_Font; Conductor_Matrix_Cell_Renderer Renderer; private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_RENDERER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Conductor_Matrix ( Conductor_Matrix_Model matrix_model ) { super (matrix_model); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Conductor_Matrix"); setColumnSelectionAllowed (false); setRowSelectionAllowed (true); setSelectionMode (ListSelectionModel.SINGLE_SELECTION); //setAutoResizeMode (JTable.AUTO_RESIZE_LAST_COLUMN); //setShowVerticalLines (false); setBackground (Colors.TABLE); getTableHeader ().setBackground (Colors.TABLE_HEADER); /* WARNING: These are JXTable specific methods. */ setColumnControlVisible (true); setSortable (true); Renderer = new Conductor_Matrix_Cell_Renderer (); setDefaultRenderer (String.class, Renderer); setDefaultRenderer (Conductor_Matrix_Model.Count.class, Renderer); setRowHeight (Renderer.Cell_Panel.getPreferredSize ().height); Emphasis_Font = getFont ().deriveFont (Font.BOLD); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Conductor_Matrix"); } /*============================================================================== Accessors */ /** Test if Conductor processing state count annotations are enabled.

@return true if processing state count annotations will be displayed; false otherwise. @see #State_Annotate(boolean) */ public boolean State_Annotate () {return State_Annotate;} /** Enable or disable Conductor processing state count annotations.

When enable each Conductor processing state count label includes a single character annotation corresponding to the first character of its state name: running, polling, waiting, halted and total.

When disabled the annotatations are not included in the label.

@param enable If true processing state count annotations will be displayed. If false annotations are not displayed. @return This Conductor_Matrix. */ public Conductor_Matrix State_Annotate ( boolean enable ) { if (State_Annotate != enable) { State_Annotate = enable; repaint (); } return this; } /** Get the Conductor processing state counts that are shown in the table.

@return A bit mask indicating which Conductor processing state counts are shown in the table. @see #Counts_Shown(int) */ public int Counts_Shown () {return Counts_Shown;} /** Set the Conductor processing state counts that are shown in the table.

Conductor processing state counts to be shown are selected by a bit mask composed of the logic OR-ing of the following values:

{@link #RUNNING_COUNT}
Conductors that are running: Source records are currently being processed.
{@link #POLLING_COUNT}
Conductors that are polling: The Conductor is running but no unprocessed source records are available from the database so the Conductor is periodically pollling for additional unprocessed source records.
{@link #WAITING_COUNT}
Conductors that are waiting: The Conductor has stopped processing source records by request, or it has yet to start, and is waiting to be told to start running.
{@link #HALTED_COUNT}
Conductors that have halted: The Conductor has stopped processing due to the sequential source record processing failures limit having been reached or an error condition was encountered.
{@link #TOTAL_COUNT}
The total number of Conductors of the corresponding name on the corresponding Theater.
{@link #ALL_COUNTS}
All of the above counts. This is the default.

@param counts_shown A bit mask int value that is the OR-ing of processing state count selection flags. If zero {@link #ALL_COUNTS} will be used. @return This Conductor_Matrix. */ public Conductor_Matrix Counts_Shown ( int counts_shown ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Conductor_Matrix.Counts_Shown: " + counts_shown); if (Renderer.Counts_Shown (counts_shown)) repaint (); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Conductor_Matrix.Counts_Shown"); return this; } /* If not provided by the base class. public int convertRowIndexToModel ( int view_row ) {return view_row;} */ /*============================================================================== Conductor_Matrix_Cell_Renderer */ private class Conductor_Matrix_Cell_Renderer implements TableCellRenderer { private JPanel Cell_Panel; private JLabel State_Labels[] = new JLabel[TOTAL_INDEX + 1]; private JLabel Name_Label = new JLabel (), Empty_Label = new JLabel (); private Color Background_Color; private static final int BORDER_WIDTH = 2; public Conductor_Matrix_Cell_Renderer () { // Conductor names label. Name_Label.setOpaque (true); // Empty cell label. Empty_Label.setOpaque (true); // Panel to contain the processing state counts cells. Cell_Panel = new JPanel (new GridBagLayout ()); Cell_Panel.setOpaque (true); Cell_Panel.setBackground (Colors.TABLE); // Processing state counts cell labels. JLabel label; for (int index = 0; index < TOTAL_INDEX; index++) { label = new JLabel (" ", SwingConstants.RIGHT); label.setVerticalAlignment (SwingConstants.CENTER); label.setOpaque (true); label.setBorder (BorderFactory.createCompoundBorder (BorderFactory.createBevelBorder (BevelBorder.RAISED, Color.WHITE, Color.GRAY), BorderFactory.createLineBorder (STATE_COLORS[index][0], BORDER_WIDTH))); State_Labels[index] = label; } label = new JLabel (" ", SwingConstants.RIGHT); label.setVerticalAlignment (SwingConstants.CENTER); label.setOpaque (true); label.setBorder (BorderFactory.createCompoundBorder (BorderFactory.createBevelBorder (BevelBorder.RAISED, Color.WHITE, Color.GRAY), BorderFactory.createEmptyBorder (BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH, BORDER_WIDTH))); State_Labels[TOTAL_INDEX] = label; // Assemble the initial contents of the cell panel. Counts_Shown (ALL_COUNTS); } public boolean Counts_Shown ( int counts_shown ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (">>> Conductor_Matrix_Cell_Renderer.Counts_Shown: " + counts_shown); counts_shown &= ALL_COUNTS; if (counts_shown == 0) counts_shown = TOTAL_COUNT; if (Counts_Shown == counts_shown) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("<<< Conductor_Matrix_Cell_Renderer.Counts_Shown: false"); return false; } if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" New Counts_Shown = " + counts_shown); Counts_Shown = counts_shown; Cell_Panel.removeAll (); GridBagConstraints location = new GridBagConstraints (); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; int index = -1; while (++index <= TOTAL_INDEX) if ((Counts_Shown & (1 << index)) != 0) Cell_Panel.add (State_Labels[index], location); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("<<< Conductor_Matrix_Cell_Renderer.Counts_Shown: true"); return true; } public Component getTableCellRendererComponent ( JTable table, Object value, boolean selected, boolean focused, int row, int column ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (">>> Conductor_Matrix.getTableCellRendererComponent:" + NL +" Value - " + value + NL +" Selected - " + selected + NL +" Table row " + row + " column " + column); row = ((JXTable)table).convertRowIndexToModel (row); column = table.convertColumnIndexToModel (column); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" Model row " + row + " column " + column); if (value == null) value = "null"; Component component = null; if ((row % 2) != 0) Background_Color = selected ? Colors.ALTERNATE_SELECTED : Colors.ALTERNATE; else if (column == 0) Background_Color = selected ? Colors.TABLE_HEADER_SELECTED : Colors.TABLE_HEADER; else Background_Color = selected ? Colors.TABLE_SELECTED : Colors.TABLE; if (column == 0) { component = Name_Label; Name_Label.setText (value.toString ()); Name_Label.setBackground (Background_Color); } else if (value instanceof Conductor_Matrix_Model.Count) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" Counts_Shown: " + Counts_Shown + (((Counts_Shown & RUNNING_COUNT) != 0) ? " RUNNING" : "") + (((Counts_Shown & POLLING_COUNT) != 0) ? " POLLING" : "") + (((Counts_Shown & WAITING_COUNT) != 0) ? " WAITING" : "") + (((Counts_Shown & HALTED_COUNT) != 0) ? " HALTED" : "") + (((Counts_Shown & TOTAL_COUNT) != 0) ? " TOTAL" : "") ); Conductor_Matrix_Model.Count count = (Conductor_Matrix_Model.Count)value; if (count.Counts[TOTAL_INDEX] == 0) { component = Empty_Label; Empty_Label.setBackground (Background_Color); } else { component = Cell_Panel; int index = -1; while (++index <= TOTAL_INDEX) if ((Counts_Shown & (1 << index)) != 0) Format (index, count.Counts[index], selected); } } else { component = Name_Label; Name_Label.setText (value.toString ()); Name_Label.setBackground (Background_Color); } if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("<<< Conductor_Matrix.getTableCellRendererComponent"); return component; } private void Format ( int index, int value, boolean selected ) { String text; Color color = Background_Color; if (value == 0) text = " "; else { text = String.valueOf (value); if (index < TOTAL_INDEX) color = selected ? STATE_COLORS[index][1] : STATE_COLORS[index][0]; } if (State_Annotate) text += STATE_ANNOTATIONS[index]; State_Labels[index].setText (text); State_Labels[index].setBackground (color); } } // Conductor_Table_Cell_Renderer } pirl-2.3.8/PIRL/Conductor/Maestro/Theater_List_Model.java0000644000175000017500000005222011742733134023044 0ustar mathieumathieu/* Theater_List_Model PIRL CVS ID: Theater_List_Model.java,v 1.29 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Messenger.Messenger; import PIRL.Utilities.Host; import javax.swing.AbstractListModel; import javax.swing.MutableComboBoxModel; import java.util.List; import java.util.Vector; /** A Theater_List_Model extends the AbstractListModel to provide management of a list of Theater objects.

@author Bradford Castalia - UA/PIRL @version 1.29 */ public class Theater_List_Model extends AbstractListModel implements MutableComboBoxModel { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Theater_List_Model (1.29 2012/04/16 06:04:12)"; /* List of Theaters and their locations. All Locations should be non-null, unique Strings. Each location should be a fully qualified Theater.Location(). The Names - the Theater.Location(String) portion from the location - are provided for display. If the name is the same as some other name, all the identical names are reset to their fully qualified location versions. The Theaters are Theater objects that correspond to the Locations. A Location may have a null theater. Every non-null theater should have a Theater.Location () identical to its corresponding Locations entry. */ private Vector Names = new Vector (), Locations = new Vector (); private Vector Theaters = new Vector (); // Supports ComboBoxModel interface {set,get}SelectedItem methods. private String Selected_Location = null, Previous_Location = null; private static final String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_ADD = 1 << 2, DEBUG_LOCATION = 1 << 3, DEBUG_SELECTION = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== MutableComboBoxModel */ public void addElement ( Object element ) { if (element == null) return; if (element instanceof Theater) Add ((Theater)element); else if (element instanceof String) { String location = (String)element; if (Index (location) < 0) { // New location. int index = getSize (); if (Location (null, location, index)) fireContentsChanged (this, -1, -1); else fireIntervalAdded (this, index, index); } } } public void insertElementAt ( Object element, int index ) { if (element == null || index < 0 || index > getSize ()) return; Theater theater = null; String location = null; if (element instanceof Theater) { theater = (Theater)element; if (Index (theater) >= 0) // The theater is already present. return; location = theater.Location (); } else if (element instanceof String) { location = (String)element; if (Index (location) >= 0) // The location is already present. return; } if (location != null) { Names.insertElementAt (null, index); Locations.insertElementAt (null, index); Theaters.insertElementAt (null, index); if (Location (theater, location, index)) fireContentsChanged (this, -1, -1); else fireIntervalAdded (this, index, index); } } public void removeElement ( Object element ) { if (element == null) return; if (element instanceof Theater) Remove ((Theater)element); else if (element instanceof String) Remove (Index ((String)element)); } public void removeElementAt ( int index ) {Remove (index);} /*------------------------------------------------------------------------------ ComboBoxModel */ /** Set the currently selected item.

If the item is null nothing is done.

If the item is a Theater its {@link Theater#Location() location} is used. Otherwise the item must be a location String. The location is converted to the {@link Theater#Location(String) abbreviated location} form.

If the item location is equal to the currently {@link #getSelectedItem() selected item location} nothing is done.

The {@link #Previous_Location() previously selected item location} is set to the currently selected item location and the currently selected item location is then set to the new item location. All listeners are notified that the selected item has changed.

@param item A Theater or location String. */ public void setSelectedItem ( Object item ) { if (item == null) return; String location = null; if (item instanceof Theater) location = ((Theater)item).Location (); else if (item instanceof String) location = (String)item; location = Theater.Location (location); if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">-< Theater_List_Model.setSelectedItem: " + location); if (location != null && ! location.equals (Selected_Location)) { Previous_Location = Selected_Location; Selected_Location = location; fireContentsChanged (this, -1, -1); } } /** Get the currently selected item location.

N.B.: The abbreviated form of the Theater location is returned.

@return The currently selected item location String. This will be null if, and only if, no item has yet been selected. @see Theater#Location(String) @see Theater#Full_Location(String) */ public Object getSelectedItem () { if ((DEBUG & DEBUG_SELECTION) != 0) System.out.println (">-< Theater_List_Model.getSelectedItem: " + Selected_Location); return Selected_Location; } /** Get the previously selected item location.

Each time the currently selected item is {@link #setSelectedItem(Object) updated} with a new value the previously selected item is set to the currently selected item before it is changed.

N.B.: The abbreviated form of the Theater location is returned.

@return The previously selected item location String. This will always be different than the {@link #getSelectedItem() currently selected item location} unless both are null. This will be null until the second change of the currently selected item. */ public String Previous_Location () {return Previous_Location;} /*------------------------------------------------------------------------------ ListModel */ /** Get the Theater name at the data model index.

@param index A data model index. @return The Theater name for the data model idex, If the index is not valid null is returned. */ public Object getElementAt ( int index ) { if (index >= 0 && index < Names.size ()) return Names.get (index); return null; } /** Get the number of entries in the data model.

@return The total number of entries in the data model. @see #Total_Opened() */ public int getSize () {return Theaters.size ();} /*============================================================================== Accessors */ /** Add a Theater to the data model.

If a Theater at the {@link Theater#Location() location} of the specified Theater is already in the data model that entry is replaced. Otherwise a new entry for the specified Theater is added to the data model.

If addition of the specified Theater resulted in a {@link #Location(Theater, String, int) location name change}, notification is sent to all listeners that the entire list may have changed; otherwise notification of a change to only the affected data model index is sent.

@param theater A Theater to be added to the data model. If null nothing is done. @return This Theater_List_Model. */ public Theater_List_Model Add ( Theater theater ) { if ((DEBUG & DEBUG_ADD) != 0) System.out.println (">>> Theater_List_Model.Add:" + NL + theater); if (theater == null) { if ((DEBUG & DEBUG_ADD) != 0) System.out.println ("<<< Theater_List_Model.Add"); return this; } String location = theater.Location (); int size = getSize (), index = Index_of (location); if (index < 0) { // New theater location. index = size; if ((DEBUG & DEBUG_ADD) != 0) System.out.println (" New Theater"); } if (Location (theater, location, index)) fireContentsChanged (this, -1, -1); else if (index == size) fireIntervalAdded (this, index, index); else fireContentsChanged (this, index, index); if ((DEBUG & DEBUG_ADD) != 0) System.out.println ("<<< Theater_List_Model.Add"); return this; } /** Set a location at a data model index.

If the theater is non-null the {@link Theater#Location() Theater location} overrides the specified location. Otherwise, if the specified location is non-null it is set to the {@link Theater#Full_Location(String) standard location format}. The {@link Theater#Location(String) abbreviated theater name} is obtained from the location.

If the name is different from the location the names list is searched for names not at the specified index that match the new name. If a match is found a name changed flag is set and all matching names are reset to the location (the name is changed to the location). This ensures that names will always be unique.

If the specified index equals the list size - the location is a new addition to the list - the name and location are added to their lists and a null place-holder entry is added to the Theaters list. Otherwise the location replaces an existing location and the name replaces the existing name.

The name changed flag is returned as a signal that a notification should be sent that the entire list may have changed due to the name change; the change is not limited to the specified index.

@param theater A Theater object. If null and the location is null nothing is done and false is returned. @param location A Theater location String. If null or empty the {@link Theater#Location() Theater location} is used. @param index A data model index where the location is to be set. If less than zero or greater than the list size nothing is done and false is returned. @return true if the list of Theater names was changed - the change is not limited to the specified index; false otherwise. */ protected boolean Location ( Theater theater, String location, int index ) { if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println (">>> Theater_List_Model.Location:" + NL +" theater -" + NL + theater + NL +" location - " + location + NL +" index - " + index); if (index < 0 || index > getSize ()) { if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println ("<<< Theater_List_Model.Location: false"); return false; } String name = Theater.Location (location); // Ensure that the standard location format is used. if (theater != null) location = theater.Location (); else if ((location = Theater.Full_Location (location)) == null) { if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println ("<<< Theater_List_Model.Location: false"); return false; } if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println (" Theater name: " + name + NL +" Theater location: " + location); // Check for duplicate names. boolean name_change = false; if (! name.equals (location)) { for (int name_index = 0; (name_index = Names.indexOf (name, name_index)) >= 0; name_index++) { if (name_index != index) { // Duplicate name. Use the location. Names.set (name_index, location); name_change = true; } } if (name_change) { name = location; if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println (" Duplicate name; using location"); } } if (index == getSize ()) { if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println (" Add new entries"); Names.add (name); Locations.add (location); Theaters.add (theater); if (index == 0 && Selected_Location == null) Selected_Location = name; } else { if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println (" Set existing entries"); Names.set (index, name); Locations.set (index, location); Theaters.set (index, theater); } if ((DEBUG & DEBUG_LOCATION) != 0) System.out.println ("<<< Theater_List_Model.Location: " + name_change); return name_change; } public Theater_List_Model Replace ( List theaters ) { Theater theater; boolean changed = false; int index, row = getSize (); while (--row >= 0) { // Find the current theater in the new theaters list. if ((theater = Theaters.get (row)) == null) // No theater. continue; index = Matching_Index (theater, theaters); if (index < 0) { // Obsolete: Current theater is not in the new list. Remove_at (row); changed = true; } else // Redundant: Current theater is in the new list. theaters.remove (index); } if (theaters.size () > 0) { // What's left are new theaters. index = -1; row = theaters.size (); while (++index < row) Location (theaters.get (index), null, getSize ()); changed = true; } if (changed) fireContentsChanged (this, -1, -1); return this; } public Theater Remove ( int index ) { Theater theater = null; if (index >= 0 && index < getSize ()) { theater = Theaters.get (index); Remove_at (index); fireIntervalRemoved (this, index, index); } return theater; } private void Remove_at ( int index ) { if (Names.get (index).equals (Selected_Location)) { if (index == 0) Selected_Location = (Locations.size () == 1) ? null : Names.get (1); else Selected_Location = Names.get (index - 1); } Theaters.remove (index); Locations.remove (index); Names.remove (index); } public Theater Remove ( Theater theater ) {return Remove (Index (theater));} /** Clear the data model of all entries.

N.B.: The Theaters are not {@link #Close(Theater) closed}.

@see #Close_All() */ public void Clear () { Selected_Location = null; if (getSize () > 0 ) { int size = getSize () - 1; Theaters.clear (); Locations.clear (); Names.clear (); fireIntervalRemoved (this, 0, size); } } //------------------------------------------------------------------------------ public int Index ( Theater theater ) { if (theater != null) { int index = getSize (); while (--index >= 0) if (theater == Theaters.get (index)) return index; } return -1; } public int Index ( String location ) { if (location != null && location.length () != 0) // Ensure a standard location format. return Index_of (Theater.Full_Location (location)); return -1; } private int Index_of ( // Standard location format. String location ) { int index = Locations.size (); while (--index >= 0) if (location.equals (Locations.get (index))) break; return index; } /** Get the index of a Theater in a list of Theaters that matches.

Theaters match when their {@link Theater#Location() locations} are equal.

@param theater A Theater to be matched. @param theaters A List of Theater objects. @return The index of the first matching Theater in the list. */ protected static int Matching_Index ( Theater theater, List theaters ) { if (theater == null || theaters == null) return -1; String location = theater.Location (); int index = -1, size = theaters.size (); while (++index < size) if ((theater = theaters.get (index)) != null && theater.Location ().equals (location)) return index; return -1; } public Theater Theater ( int index ) { if (index >= 0 && index < getSize ()) return Theaters.get (index); return null; } public Theater Theater ( String location ) {return Theater (Index (location));} public Theater Theater ( Messenger messenger ) { if (messenger == null) return null; Theater theater; int index = -1, size = getSize (); while (++index < size) if ((theater = Theaters.get (index)) != null && theater.Messenger () == messenger) return theater; return null; } /** Get the Theater for a Messenger address.

@param messenger_address A Messenger {@link Messenger#Address() address}. @return The Theater for the Messenger address. This will be null the Messenger address is null or no Theater with a Messenger having the specified address can be found. */ public Theater Messenger_Theater ( String messenger_address ) { if (messenger_address == null) return null; Theater theater; int index = -1, size = getSize (); while (++index < size) if ((theater = Theaters.get (index)) != null && theater.Messenger ().Address ().equals (messenger_address)) return theater; return null; } /** Get the Theater location for a data model index.

@param index A data model index. @return The Theater location String for the data model index. This will be null if the index is invalid. N.B.: This will be the same as the {@link Theater#Location() Theater location} for the Theater at the same index if the index has a Theater; but since there may be no Theater for an index the locations list is used instead. */ public String Location ( int index ) { if (index >= 0 && index < getSize ()) return Locations.get (index); return null; } public String Location ( Messenger messenger ) {return Location (Theater (messenger));} public String Location ( String messenger_address ) {return Location (Messenger_Theater (messenger_address));} /** Get the location for a Theater in the data model.

@param theater A Theater. @return The location from the data model list associated with the Theater. This will be null if the theater is not in the data model. */ public String Location ( Theater theater ) { String location = null; int index = Index (theater); if (index >= 0) location = Locations.get (index); return location; } /** Set the maximum amount of time, in seconds, that will be used when waiting for a Theater protocol Message to be received.

@param timeout The {@link Theater#Receive_Timeout(int) timeout value} to be applied to all Theaters in the data model. */ public void Receive_Timeout ( int timeout ) { Theater theater; for (int index = 0; index < getSize (); index++) { theater = Theaters.get (index); if (theater != null) theater.Receive_Timeout (timeout); } } public boolean Opened ( int index ) { if (index >= 0 && index < getSize ()) { Theater theater = Theaters.get (index); return theater != null && theater.Opened (); } return false; } public boolean Opened ( String location ) {return Opened (Index (location));} public int Total_Opened () { Theater theater; int total = 0, index = getSize (); while (--index >= 0) if ((theater = Theaters.get (index)) != null && theater.Opened ()) ++total; return index; } public void Update ( int index ) {fireContentsChanged (this, index, index);} public void Update () {fireContentsChanged (this, 0, getSize () - 1);} /** Close a Theater at an index in the data model.

If the Theater is closed (the return value is true) a {@link #fireContentsChanged(Object, int, int) change notification} is sent to all {@link #addListDataListener(ListDataListener) listeners}.

@param index A data model index. If the index is invalid or no Theater is present at the index nothing is done. @return true if an open theater was present at the index and it was closed; false otherwise. */ public boolean Close ( int index ) { boolean closed = Close_Theater (Theater (index)); if (closed) fireContentsChanged (this, index, index); return closed; } /** Close a Theater registered in the data model.

If the Theater is closed (the return value is true) a {@link #fireContentsChanged(Object, int, int) change notification} is sent to all {@link #addListDataListener(ListDataListener) listeners}.

@param theater A Theater to be closed. N.B.: If the theater is not registered in this data model nothing is done. @return true if the theater is in this data model and it was closed, having been found to be open; false otherwise. */ public boolean Close ( Theater theater ) { boolean closed = false; int index = Index (theater); if (index >= 0) closed = Close_Theater (theater); if (closed) fireContentsChanged (this, index, index); return closed; } /** Close all Theaters registered in the data model.

If any Theater is closed (the return value is true) a {@link #fireContentsChanged(Object, int, int) change notification} is sent to all {@link #addListDataListener(ListDataListener) listeners}.

@return true if any Theater in the data model was found to be open and was closed; false if there was no Theater to be closed. */ public boolean Close_All () { boolean closed = false; int index = Theaters.size (); while (--index >= 0) if (Close_Theater (Theaters.get (index))) closed = true; if (closed) fireContentsChanged (this, -1, -1); return closed; } private static boolean Close_Theater ( Theater theater ) { if (theater != null) return theater.Close (); return false; } } pirl-2.3.8/PIRL/Conductor/Maestro/Stage_Manager_Watchdog0000755000175000017500000000313511077044046022734 0ustar mathieumathieu#!/bin/csh # # Stage Manager Watchdog # # # CVS ID: Stage_Manager_Watchdog,v 1.4 2008/10/20 08:58:14 castalia Exp set STAGE_MANAGER = /opt/local/sh/Stage_Manager set CONFIG_FILE = /etc/Stage_Manager.conf set LOG_DIR = /var/log/Stage_Manager set LOG_FILE = $LOG_DIR/Stage_Manager.log set PID_FILE = $LOG_DIR/Stage_Manager.pid set POLL_INTERVAL = 10 # Initialize the PID; from an existing PID file if available. if (-f $PID_FILE) then set PID = `cat $PID_FILE` else set PID = $STAGE_MANAGER endif while (1) # Watchdog: Will only exit if the Stage_Manager can not be started. # Check if the process is running. set PIDs = (`/bin/ps auxwww \ | /bin/grep -v grep \ | /bin/awk '{print $2}' \ | /bin/grep "$PID"`) while ($#PIDs) if ($PIDs[1] == $PID) then break endif shift PIDs end if (! $#PIDs) then # The process was not found. Attempt to start the $STAGE_MANAGER. # Remove the PID file. /bin/rm -f $PID_FILE # Run the Stage_Manager and capture its PID. echo >>! $LOG_FILE /bin/date "+%e %B %G %T %z" >> $LOG_FILE $STAGE_MANAGER -Configuration $CONFIG_FILE >>& $LOG_FILE & set PID = $! # Confirm that the Stage_Manager started successfully. sleep 3 set PIDs = (`/bin/ps auxwww \ | /bin/grep -v grep \ | /bin/awk '{print $2}' \ | /bin/grep "$PID"`) while ($#PIDs) if ($PIDs[1] == $PID) then break endif shift PIDs end # Update the PID file. if ($#PIDs) then echo $PID >! $PID_FILE else echo "The Stage_Manager did not start successfully" >! $PID_FILE exit 1 endif endif # Wait for the poll interval. sleep $POLL_INTERVAL end pirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Table.java0000644000175000017500000001101011742733133022373 0ustar mathieumathieu/* Conductor_Table PIRL CVS ID: Conductor_Table.java,v 1.15 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Conductor; import PIRL.Conductor.Colors; import org.jdesktop.swingx.JXTable; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.table.TableCellRenderer; import javax.swing.JLabel; import javax.swing.BorderFactory; import java.awt.Component; import java.awt.Color; import java.awt.Font; /** A Conductor_Table is a table view of a Conductor_Table_Model.

@author Bradford Castalia, UA/PIRL @version 1.15 */ public class Conductor_Table extends JXTable { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Table (1.15 2012/04/16 06:04:11)"; public static final int RUN_TO_WAIT_STATE = Conductor.RUN_TO_WAIT, RUNNING_STATE = Conductor.RUNNING, POLLING_STATE = Conductor.POLLING, WAITING_STATE = Conductor.WAITING, HALTED_STATE = Conductor.HALTED; private Font Emphasis_Font; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_RENDERER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Conductor_Table ( Conductor_Table_Model table_model ) { super (table_model); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Conductor_Table"); //setColumnSelectionAllowed (false); //setRowSelectionAllowed (true); setSelectionMode (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); setAutoResizeMode (JTable.AUTO_RESIZE_LAST_COLUMN); setBackground (Colors.TABLE); Emphasis_Font = getFont ().deriveFont (Font.BOLD); getTableHeader ().setBackground (Colors.TABLE_HEADER); /* WARNING: This is a JXTable specific method. */ setColumnControlVisible (true); setDefaultRenderer (String.class, new Conductor_Table_Cell_Renderer ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Conductor_Table"); } /*============================================================================== Accessors */ /* If not provided by the base class. public int convertRowIndexToModel ( int view_row ) {return view_row;} public int convertRowIndextoView ( int model_row ) {return model_row;} */ /*============================================================================== Conductor_Table_Cell_Renderer */ private class Conductor_Table_Cell_Renderer extends JLabel implements TableCellRenderer { public Conductor_Table_Cell_Renderer () { setOpaque (true); //setFont (Emphasis_Font); setBorder (BorderFactory.createEmptyBorder (0, 5, 0, 0)); } public Component getTableCellRendererComponent ( JTable table, Object value, boolean selected, boolean focused, int row, int column ) { row = ((JXTable)table).convertRowIndexToModel (row); column = table.convertColumnIndexToModel (column); Color color = table.getBackground (); if (column == 1) { switch (((Conductor_Table_Model)table.getModel ()).Processing_State (row)) { case RUN_TO_WAIT_STATE: color = Colors.RUN_TO_WAIT; break; case RUNNING_STATE: color = Colors.RUNNING_STATE; break; case POLLING_STATE: color = Colors.POLLING_STATE; break; case WAITING_STATE: color = Colors.WAITING_STATE; break; case HALTED_STATE: color = Colors.HALTED_STATE; break; } } setBackground (Colors.Selected_Color (selected, color)); setValue (value); return this; } public void setValue ( Object value ) {setText ((value == null) ? "" : value.toString ());} } // Conductor_Table_Cell_Renderer } pirl-2.3.8/PIRL/Conductor/Maestro/Manage_Stage_Manager0000755000175000017500000001043211547145633022370 0ustar mathieumathieu#!/bin/csh # # Manage a Stage Manager # # CVS ID: Manage_Stage_Manager,v 1.9 2011/04/06 20:22:51 castalia Exp # Initial environment setup: # Workaround obsolete cruft in the PIRL User Environment setups (.login). setenv NO_TERM_MENU 1 set TERM = non_interactive source ~/.cshrc source ~/.login # Required STAGE_MANAGER. if (! $?STAGE_MANAGER) then set STAGE_MANAGER = /opt/local/sh/Stage_Manager endif # Required STAGE_MANAGER_LOG_DIR. if (! $?STAGE_MANAGER_LOG_DIR) then set STAGE_MANAGER_LOG_DIR = ~/.Stage_Manager endif # Arguments parsing: set Stage_Manager_Args = () while ($#argv) switch ($1) case [Ss][Tt][Aa]*: set op = "start" goto Set_Operation case [Ss][Tt][Oo]*: set op = "stop" goto Set_Operation case [Rr][Ee]*: set op = "restart" Set_Operation: if ($?Operation) then if ($Operation != $op) then echo "Multiple operations - $Operation and $op" echo goto Usage endif endif set Operation = $op breaksw case -[Hh]*: goto Usage default: set Stage_Manager_Args = ($Stage_Manager_Args $1) breaksw endsw shift end # Help listing: if (! $?Operation) then Usage: echo "Usage: $0:t [Stage_Manager arguments]" echo echo 'Manage a Stage_Manager on the local host system.' echo 'Operation is one of -' echo ' start' echo ' stop' echo ' restart' echo echo 'All other arguments are passed to the Stage_Manager.' echo echo 'Environment setup:' echo echo 'The ~/.cshrc and ~/.login files are sourced.' echo 'If a $STAGE_MANAGER_LOG_DIR/Environment' echo ' or $STAGE_MANAGER_LOG_DIR/.environment file exists it is sourced.' echo echo 'The $HOST.{pid,report,log} files are written to the $STAGE_MANAGER_LOG_DIR.' echo echo "STAGE_MANAGER_LOG_DIR is $STAGE_MANAGER_LOG_DIR" echo "The Stage_Manager procedure is in $STAGE_MANAGER" echo $STAGE_MANAGER -help exit 1 endif # Confirm or create the STAGE_MANAGER_LOG_DIR. if (! -d $STAGE_MANAGER_LOG_DIR) then mkdir -p $STAGE_MANAGER_LOG_DIR if ($status) then echo "Unable to create the $STAGE_MANAGER_LOG_DIR directory." exit 1 endif endif # Secondary environment setup: # Avoid possible reset of STAGE_MANAGER or STAGE_MANAGER_LOG_DIR. set Stage_Manager = $STAGE_MANAGER set Stage_Manager_Log_File = $STAGE_MANAGER_LOG_DIR/$HOST.log set Stage_Manager_PID_File = $STAGE_MANAGER_LOG_DIR/$HOST.pid set Stage_Manager_Report_File = $STAGE_MANAGER_LOG_DIR/$HOST.report if (-f $STAGE_MANAGER_LOG_DIR/Environment) then source $STAGE_MANAGER_LOG_DIR/Environment else if (-f $STAGE_MANAGER_LOG_DIR/.environment) then source $STAGE_MANAGER_LOG_DIR/.environment endif # Initialize the PID; from an existing PID file if available. if (-f $Stage_Manager_PID_File) then set PID = `cat $Stage_Manager_PID_File` # Check if the process is running. set PIDs = (`/bin/ps auxwww \ | grep -v grep \ | awk '{print $2}' \ | grep "$PID"`) while ($#PIDs) if ($PIDs[1] == $PID) then break endif shift PIDs end if (! $#PIDs) then # The process was not found. unset PID endif endif if ($Operation == "stop" || $Operation == "restart") then if ($?PID) then echo "Shutting down Stage_Manager $PID" /bin/kill -TERM $PID /bin/rm -f $Stage_Manager_PID_File unset PID else echo "No Stage_Manager to stop" endif endif if ($Operation == "start" || $Operation == "restart") then if ($?PID) then echo "Stage_Manager $PID is already running" exit 0 endif # Remove the LOG, PID and REPORT FILEs. /bin/rm -f $Stage_Manager_Log_File /bin/rm -f $Stage_Manager_PID_File /bin/rm -f $Stage_Manager_Report_File # Run the Stage_Manager and capture its PID. echo "Starting Stage_Manager -log $Stage_Manager_Log_File $Stage_Manager_Args" /bin/date "+%e %B %G %T %z" > $Stage_Manager_Report_File /usr/bin/nohup $Stage_Manager -log $Stage_Manager_Log_File $Stage_Manager_Args:q >>& $Stage_Manager_Report_File & set PID = $! # Confirm that the Stage_Manager started successfully. sleep 3 set PIDs = (`/bin/ps auxwww \ | grep -v grep \ | awk '{print $2}' \ | grep "$PID"`) while ($#PIDs) if ($PIDs[1] == $PID) then break endif shift PIDs end # Update the PID file. if ($#PIDs) then echo $PID >! $Stage_Manager_PID_File echo "Started Stage_Manager $PID" else echo "The Stage_Manager did not start successfully" exit 1 endif endif exit 0 end pirl-2.3.8/PIRL/Conductor/Maestro/package.html0000644000175000017500000000426711742733134020763 0ustar mathieumathieu The Maestro package provides applications for remote management of Conductor networks.

Stage_Manager is a {@link PIRL.Messenger.Dispatcher} application. It extends the Dispatcher with Conductor excecution capabilities. The Stage_Manager acts as a Message communications proxy server between Conductor clients in its Theater and Kapellmeister clients used to manage Conductors in one or more Theaters.

A Local_Theater is used by a Conductor for remote {@link PIRL.Conductor.Management} interface access via a {@link PIRL.Messenger.Messenger} connected to a Stage_Manager using the Theater Message protocol.

A Remote_Theater is a Stage_Manager Dispatcher client that implements the Management interface using the Theater Message protocol.

The Kapellmeister (Master of the Chapel) is an application that provides a GUI tool for management of Theaters. Each Theater corresponds to a connection to a Stage_Manager. Each Stage_Manager notifies each connected Kapellmeister of all its Conductor clients. The Kapellmeister establishes direct linked Messenger communciation with each Conductor for monitoring and managment of the Conductors. @see PIRL.Conductor pirl-2.3.8/PIRL/Conductor/Maestro/Local_Theater.java0000644000175000017500000010722611742733133022051 0ustar mathieumathieu/* Local_Theater PIRL CVS ID: Local_Theater.java,v 1.41 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Management; import PIRL.Conductor.Processing_Listener; import PIRL.Conductor.Processing_Event; import PIRL.Messenger.*; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import PIRL.Utilities.Styled_Multiwriter; import PIRL.Utilities.Suspendable_Styled_Writer; import java.io.Writer; import java.io.IOException; import java.net.ConnectException; import java.net.MulticastSocket; import java.net.DatagramPacket; import java.net.InetAddress; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.Vector; /** A Local_Theater is the local Conductor management side of a {@link Theater}.

A Local_Theater is used by a {@link PIRL.Conductor.Conductor} to provide network distributed clients access to its {@link PIRL.Conductor.Management} interface via a {@link Stage_Manager} proxy server.

A Local_Theater implements the {@link Message_Delivered_Listener} interface. Each {@link Message} delivered via a Stage_Manager {@link Messenger} is examined for an {@link Message#Action() action} parameter that determines how the remaining content will be mapped to a Management method of the Conductor bound to the Local_Theater, and any return values used to assemble the appropriate {@link Message#Reply_To(Message) reply} sent back.

A Local_Theater also implements the {@link Processing_Listener} interface that receives {@link Processing_Event} notifications from the Conductor bound to the Local_Theater. The {@link PIRL.Conductor.Processing_Changes} of the event are used to assemble the appropriate Message that is sent to all clients that have registered, via a Message with a {@link Theater#ADD_PROCESSING_LISTENER_ACTION}, an interest in the Conductor's processing events. In addition, any clients that have requested, via a Message with a {@link Theater#ADD_LOG_WRITER_ACTION}, to receive the Conductor processing log stream will have the stream sent via Messages.

@author Bradford Castalia - UA/PIRL @version 1.41 */ public class Local_Theater extends Theater implements Message_Delivered_Listener, Processing_Listener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Local_Theater (1.41 2012/04/16 06:04:11)"; private Management The_Management; private String Theater_Key = null; /** The name of the Theater Message protocol processing Thread. */ public static final String ASSISTANT_NAME = "Local_Theater Assistant"; private Assistant Assistant = null; private final BlockingQueue Tasks = new LinkedBlockingQueue (); /** The default port on which to receive the Hello datagram. */ public static final int DEFAULT_HELLO_PORT = Dispatcher.DEFAULT_HELLO_PORT; private int Hello_Port = DEFAULT_HELLO_PORT; /** The default private multicast address to use in the Hello datagram. */ public static final String DEFAULT_HELLO_ADDRESS = Dispatcher.DEFAULT_HELLO_ADDRESS; private String Hello_Address = DEFAULT_HELLO_ADDRESS; /** The content of the Hello broadcast datagram message. */ public static final String HELLO_MESSAGE = Dispatcher.HELLO_MESSAGE; private Listen_for_Hello Hello_Listener = null; private IOException Hello_Listener_Exception = null; private boolean Auto_Open = true; // Delay (seconds) to allow the Stage_Manager to be ready for connections. private static final int AUTO_OPEN_DELAY = 4; private Vector Processing_Listener_Routes = new Vector (); private Styled_Multiwriter Log_Writer = new Styled_Multiwriter (); /** The Theater Message protcol error statistics. */ protected int Undeliverable_Messages = 0, NACK_Messages = 0, Unrecognized_Messages = 0; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONNECTION = 1 << 1, DEBUG_MESSAGES = 1 << 2, DEBUG_PROCESSING_LISTENER = 1 << 3, DEBUG_LOG_WRITER = 1 << 4, DEBUG_HELLO_LISTENER = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Local_Theater for a Conductor Management interface.

@param management The Conductor Management interface to use with the Theater Message protocol. @throws IllegalArgumentException If the management is null. */ public Local_Theater ( Management management ) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">-< Local_Theater"); if ((The_Management = management) == null) throw new IllegalArgumentException (ID + NL + "Can't construct without Management."); } // A Local_Theater can not operate without Management. private Local_Theater () {} /*============================================================================== Accessors */ /** Set the key to be used when {@link #Open(String) open}ing a Stage_Manager connection during {@link #Auto_Open(boolean) auto-open} operations.

@param key The Stage_Manager authentication key. @return This Local_Theater. */ public Local_Theater Key ( String key ) { Theater_Key = key; return this; } /** Test if auto-open has been enabled.

@return true if auto-open is enabled; false otherwise. @see #Auto_Open(boolean) */ public boolean Auto_Open () {return Auto_Open;} /** Enable or disable auto-open mode.

The current {@link #Hello_Listener_Port() "Hello" listener port} and {@link #Hello_Listener_Address() "Hello" listener address} will be used.

@param enable If true, auto-open is enabled; otherwise it is disabled. @return This Local_Theater. @see #Auto_Open(boolean, int, String) */ public Local_Theater Auto_Open ( boolean enable ) {return Auto_Open (enable, Hello_Port, Hello_Address);} /** Enable or disable auto-open mode.

When auto-open has been enabled this Local_Theater will attempt to stay open once it has been told to {@link #Open(String) open}. Failure to open will result in a Thread being started that will {@link #Listening_for_Hello() listen} for a {@link #HELLO_MESSAGE} broadcast on a specified port of a network multicast address. When the message is received listening stops, an attempt is made to open this Local_Theater again, and the Thread dies. In addition, if a {@link Theater#DONE_ACTION} Message is received after this Local_Theater has been opened another attempt to open will be made.

N.B.: Enabling auto-open after an open has failed or this Local_Theater is done will not cause an open to be tried again.

@param enable If true, auto-open is enabled; otherwise it is disabled. @param port The port number to use when listening for a "Hello" broadcast. If less than or equal to zero the {@link #DEFAULT_HELLO_PORT} will be used. @param address The network multicast address to use when listening for a "Hello" broadcast. If null the {@link #DEFAULT_HELLO_ADDRESS} will be used. @return This Local_Theater. */ public Local_Theater Auto_Open ( boolean enable, int port, String address ) { Auto_Open = enable; if (port <= 0) Hello_Port = DEFAULT_HELLO_PORT; else Hello_Port = port; if (address == null || address.length () == 0) Hello_Address = DEFAULT_HELLO_ADDRESS; else Hello_Address = address; return this; } /** Get the port number that will be used to listen for an {@link #Auto_Open(boolean, int, String) auto-open} "Hello" broadcast.

@return The port number on which to listen for an auto-open "Hello" broadcast. */ public int Hello_Listener_Port () {return Hello_Port;} /** Get the network multicast address that will be used to listen for an {@link #Auto_Open(boolean, int, String) auto-open} "Hello" broadcast.

@return The network multicast address String on which to listen for an auto-open "Hello" broadcast. */ public String Hello_Listener_Address () {return Hello_Address;} /** Test if this Local_Theater is listening for a {@link #Auto_Open(boolean, int, String) auto-open} "Hello" broadcast.

@return true if a Thread is running that is listening for a "Hello" broadcast; false otherwise. */ public boolean Listening_for_Hello () {return Hello_Listener == null;} /** Get any exception that occured in an {@link #Auto_Open(boolean, int, String) auto-open} "Hello" broadcast listener Thread.

The exception may be from either the "Hello" broadcast listener socket or the {@link #Open(String) open} operation. In the former case no attempt will be made to open this Local_Theater again to prevent an endless loop of auto-open broadcast listener socket errors.

@return The most recent IOException that occurred in a "Hello" broadcast listener Thread. This will be null if no exception has occured. */ public IOException Hello_Listener_Exception () {return Hello_Listener_Exception;} /** Get the number of undeliverable Theater protocol Messages that have been encountered.

@return The number of undeliverable Theater protocol Messages that have been encountered. */ public int Undeliverable_Messages () {return Undeliverable_Messages;} /** Get the number of NACK (negative acknowledgment) Messages that have been received.

@return The number of NACK Messages that have been received. */ public int NACK_Messages () {return NACK_Messages;} /** Get the number of Messages encountered that contain content not recognized by the Theater protocol.

@return The number of unrecognized Messages that have been encountered. */ public int Unrecognized_Messages () {return Unrecognized_Messages;} /** Get a description of this Local_Theater.

The description is a line containing the Local_Theater {@link #ID} followed by the base Theater {@link Theater#toString() description}.

@return The Local_Theater/Theater description String. */ public String toString () { return ID + NL + super.toString (); } /*============================================================================== Stage_Manager Connection */ /** Open this Local_Theater.

If this Local_Theater is not {@link Theater#Opened() opened} a {@link Theater#Open(Message) new connection} is attempted to the Stage_Manager port on the localhost using the {@link #Listener_Identity() identity} of the Management object provided at construction time supplemented by the specified authentication key. N.B.: The effective authenticiation key for use by auto-open operations is reset to the specified key only if the latter is non-null.

Once the connection to the Stage_Manager has been established this Local_Theater object is set as the {@link Theater#Employer(Message_Delivered_Listener) employer} of the Messenger used to {@link #Message_Delivered(Message_Delivered_Event) deliver Messages} from the Stage_Manager. Then this Messenger is told to begin {@link Messenger#Listen_for_Messages() listening for messages}.

If the Stage_Manager connection failed to open and {@link #Auto_Open(boolean, int, String) auto-open} has been enabled a Thread is started that will {@link #Listening_for_Hello() listen} for a {@link #HELLO_MESSAGE} broadcast on a specified port of a network multicast address indicating that a Stage_Manager is available to attempt the connection again. The exception that resulted from failing to open the connection is always thrown regardless of whether auto-open has been enabled.

N.B.: If this Local_Theater has already been {@link Theater#Opened() opened} nothing is done. In addition, if an auto-open {@link #Listening_for_Hello() "Hello" listener} Thread is active nothing is done even if this Local_Theater is not currently open; this prevents a open race condition.

@param key The Stage_Manager {@link #KEY_PARAMETER_NAME} parameter value String. N.B.: If this key is null @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there was a problem with any protocol Message content, including if the {@link #Listener_Identity() identity} from the Management interface object bound to this Local_Theater when it was constructed does not contain a valid PVL structure. @see #Key(String) */ public void Open ( String key ) throws IOException { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Local_Theater.Open"); if (! Opened ()) { if (Hello_Listener != null) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Hello_Listener is already running." + NL +"<<< Local_Theater.Open"); return; } if (key != null) Theater_Key = key; Message identity = null; try {identity = new Message (Listener_Identity ());} catch (PVL_Exception exception) { throw new Theater_Protocol_Exception (ID + NL + "Invalid Management identity -" + NL + Listener_Identity (), Theater_Protocol_Exception.INVALID_MESSAGE, exception); } identity.Set (KEY_PARAMETER_NAME, key); try {super.Open (identity);} catch (IOException exception) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Unable to open the Theater." + NL + exception + NL +" Hello_Listener: " + Hello_Listener); if (Auto_Open) { // Start a Hello_Listener Thread. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Starting a Listen_for_Hello thread"); Hello_Listener = new Listen_for_Hello (); Hello_Listener.start (); } throw exception; } // Start asychronous message listening. Employer (this); Listen_for_Messages (); } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Local_Theater.Open: " + Opened ()); } /** Close this Theater.

If this Theater is not {@link #Opened() open} nothing is done and false is returned.

The log writer is {@link Management#Remove_Log_Writer(Writer) removed} from the Theater Management and it is {@link Styled_Multiwriter#Remove_All() cleared}. N.B.: The Writers contained in the log writer are not closed.

This object is {@link Management#Remove_Processing_Listener(Processing_Listener) removed} from the Management and its lists of Messenger routes to receive Processing_Event notifications is cleared.

The parent Theater is then closed.

@return true if this Theater was open at the time the method was called; false if the Theater was already closed. */ public synchronized boolean Close () { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Local_Theater.Close"); if (! Opened ()) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Already closed" + NL +"<<< Local_Theater.Close: false"); return false; } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Closing and clearing local Log_Writer"); // Close down the log writers. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Removing the Log_Writer from The_Management"); The_Management.Remove_Log_Writer (Log_Writer); Log_Writer.Remove_All (); // Remove the Messenger route from the processing listeners list. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Removing this Processing_Listener from The_Management"); The_Management.Remove_Processing_Listener (this); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Clearing the Processing_Listener_Routes"); synchronized (Processing_Listener_Routes) {Processing_Listener_Routes.clear ();} // Close the Theater. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Close this Theater"); boolean closed = super.Close (); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Local_Theater.Close: " + closed); return closed; } /*============================================================================== Hello_Listener */ private class Listen_for_Hello extends Thread { public void run () { if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: Thread starting" + NL +" Port - " + Hello_Port + NL +" Address - " + Hello_Address); IOException hello_listener_exception = null; MulticastSocket socket = null; InetAddress address = null; try { address = InetAddress.getByName (Hello_Address); socket = new MulticastSocket (Hello_Port); socket.joinGroup (address); } catch (IOException exception) { if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: Unable to establish the socket -" + NL + exception); hello_listener_exception = exception; } if (hello_listener_exception == null) { byte[] buffer = new byte[32]; DatagramPacket packet = new DatagramPacket (buffer, buffer.length); try { socket.receive (packet); if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: \"" + new String (packet.getData (), 0, packet.getLength ()) + "\" message received"); } catch (IOException exception) { if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: socket.receive exception -" + NL + exception); hello_listener_exception = exception; } } Hello_Listener = null; if (socket != null) { try { socket.leaveGroup (address); socket.close (); } catch (IOException exception) {} } if (hello_listener_exception == null && Auto_Open) { // Wait for the Stage_Manager to be ready to accept connections. try {sleep (AUTO_OPEN_DELAY * 1000);} catch (InterruptedException exception) {} if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: Attempting to open the theater"); try {Open (Theater_Key);} catch (ConnectException exception) { // Another Listen_for_Hello thread may be run. if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: theater opening failed -" + NL + exception); } catch (IOException exception) { if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("==> Listen_for_Hello: theater opening failed -" + NL + exception); hello_listener_exception = exception; } } Hello_Listener_Exception = hello_listener_exception; if ((DEBUG & DEBUG_HELLO_LISTENER) != 0) System.out.println ("<== Listen_for_Hello"); } } // Listen_for_Hello /*============================================================================== Messages */ // Message_Delivered_Listener /** Get the identity of the Management object.

@return A Message containing the identity of the Management object provided at construction time. */ public Message Listener_Identity () {return The_Management.Identity ();} // Message_Delivered_Listener /** A message is delivered from the Stage_Manager.

The event containing the delivered Message, and a reference to the Messenger that delivered it, is put on a LinkedBlockingQueue. The the Local_Theater Assistant Thread is started, if it is not already running. The Assistant will take events from the queue (FIFO) and process them according to the Theater Message protocol that conveys the Conductor Management interface information.

Of particular interest is the handling of Messages with the {@link Theater#DONE_ACTION}: After {@link #Close() closing} this Local_Theater, if {@link #Auto_Open(boolean, int, String) auto-open} has been enabled an attempt is made to {@link #Open(String) open} the Local_Theater again using the current {@link #Key(String) key}.

@param event The Message_Delivered_Event containing the Message. @see PIRL.Conductor.Management */ public void Message_Delivered ( Message_Delivered_Event event ) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Local_Theater.Message_Delivered: " + event.Message.Action () + NL +" From -" + NL + (Messenger)event.getSource () + NL + ((Messenger)event.getSource ()).Identity () + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); try {Tasks.put (event);} catch (InterruptedException exception) { Undeliverable_Messages++; try {Send_Message (Message.NACK () .Add (ORIGINAL_MESSAGE_PARAMETER_NAME, event.Message) .Reply_To (event.Message));} catch (Exception e) {} } if (Assistant == null) { Assistant = new Assistant (); Assistant.start (); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Local_Theater.Message_Delivered"); } /*------------------------------------------------------------------------------ Assistant */ private class Assistant extends Thread { public Assistant () {super (ASSISTANT_NAME);} public void run () { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("==> Local_Theater.Assistant"); Message_Delivered_Event event; Messenger messenger; String string; Value value; while (true) { try {event = Tasks.take ();} catch (InterruptedException exception) {break;} messenger = (Messenger)event.getSource (); if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (NL +"--> Local_Theater.Assistant: " + event.Message.Action () + NL +" From -" + NL + messenger + NL + messenger.Identity () + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); switch (Action_Code (event.Message.Action ())) { case IDENTITY_CODE: try {Send_Message (The_Management.Identity () .Reply_To (event.Message));} catch (Exception e) {} break; case START_CODE: The_Management.Start (); break; case PROCESSING_STATE_CODE: try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, String.valueOf (The_Management.Processing_State ())) .Reply_To (event.Message));} catch (Exception e) {} break; case STOP_CODE: The_Management.Stop (); break; case CONFIGURATION_CODE: // Remove any parameters with conflicting names. while (event.Message.Remove (CONFIGURATION_PARAMETER_NAME) != null); Configuration configuration = The_Management.Configuration (); if (configuration != null) event.Message .Set (NAME_PARAMETER_NAME, configuration.Source ()) .Add (CONFIGURATION_PARAMETER_NAME, configuration); else event.Message.Set (EXPLANATION_PARAMETER_NAME, "No Configuration could be obtained."); try {Send_Message (event.Message .Reply_To (event.Message));} catch (Exception e) {} break; case SOURCES_CODE: try {Send_Message (event.Message .Set (TABLE_PARAMETER_NAME, The_Management.Sources ()) .Reply_To (event.Message));} catch (Exception e) {} break; case PROCEDURES_CODE: try {Send_Message (event.Message .Set (TABLE_PARAMETER_NAME, The_Management.Procedures ()) .Reply_To (event.Message));} catch (Exception e) {} break; case CONDUCTOR_STATE_CODE: try {Send_Message (Processing_Changes (CONDUCTOR_STATE_ACTION, The_Management.State ()) .Reply_To (event.Message));} catch (Exception e) {} break; case ADD_PROCESSING_LISTENER_CODE: Add_Processing_Listener_Route (event); break; case REMOVE_PROCESSING_LISTENER_CODE: Remove_Processing_Listener_Route (event.Message.Route_From ()); break; case ADD_LOG_WRITER_CODE: Add_Log_Writer_Route (event.Message.Route_From ()); break; case REMOVE_LOG_WRITER_CODE: Remove_Log_Writer_Route (event.Message.Route_From ()); break; case ENABLE_LOG_WRITER_CODE: if ((string = event.Message.Get (VALUE_PARAMETER_NAME)) != null) Enable_Log_Writer_Route (event.Message.Route_From (), string.equals ("true")); break; case GET_POLL_INTERVAL_CODE: try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, The_Management.Poll_Interval ()) .Reply_To (event.Message));} catch (Exception e) {} break; case SET_POLL_INTERVAL_CODE: if ((value = event.Message.Value_of (VALUE_PARAMETER_NAME)) != null) { try {The_Management.Poll_Interval ((int)value.long_Data ());} catch (PVL_Exception exception) { // The Value is not an integer. try {Send_Message (NACK ("Invalid " + SET_POLL_INTERVAL_ACTION + ' ' + VALUE_PARAMETER_NAME + ": " + value.Description (), exception));} catch (Exception e) {} } } break; case GET_RESOLVER_DEFAULT_VALUE_CODE: if ((string = The_Management.Resolver_Default_Value ()) == null) string = "null"; try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, string) .Reply_To (event.Message));} catch (Exception e) {} break; case SET_RESOLVER_DEFAULT_VALUE_CODE: if ((string = event.Message.Get (VALUE_PARAMETER_NAME)) != null) { if (string.equalsIgnoreCase ("null")) string = null; The_Management.Resolver_Default_Value (string); } break; case GET_STOP_ON_FAILURE_CODE: try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, The_Management.Stop_on_Failure ()) .Reply_To (event.Message));} catch (Exception e) {} break; case SET_STOP_ON_FAILURE_CODE: if ((value = event.Message.Value_of (VALUE_PARAMETER_NAME)) != null) { try {The_Management.Stop_on_Failure ((int)value.long_Data ());} catch (PVL_Exception exception) { // The Value is not an integer. try {Send_Message (NACK ("Invalid " + SET_STOP_ON_FAILURE_ACTION + ' ' + VALUE_PARAMETER_NAME + ": " + value.Description (), exception));} catch (Exception e) {} } } break; case SEQUENTIAL_FAILURES_CODE: try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, The_Management.Sequential_Failures ()) .Reply_To (event.Message));} catch (Exception e) {} break; case RESET_SEQUENTIAL_FAILURES_CODE: The_Management.Reset_Sequential_Failures (); break; case PROCESSING_EXCEPTION_CODE: Exception processing_exception = The_Management.Processing_Exception (); try {Send_Message (event.Message .Set (VALUE_PARAMETER_NAME, ((processing_exception == null) ? "null" : processing_exception.toString ())) .Reply_To (event.Message));} catch (Exception e) {} break; case QUIT_CODE: The_Management.Quit (); break; // Basic Messages: case MESSENGERS_REPORT_CODE: // Ignored. break; case DONE_CODE: if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("==> Local_Theater.Message_Delivered: Done"); Close (); if (Auto_Open) { // Let the dust settle. try {sleep (AUTO_OPEN_DELAY * 1000);} catch (InterruptedException exception) {} if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Re-opening (Auto_Open) the Theater"); try {Open (Theater_Key);} catch (IOException exception) {} } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<== Local_Theater.Message_Delivered: Done"); break; case UNDELIVERABLE_CODE: Undeliverable_Messages++; /* The problem is knowing when to take action to prevent constantly repeating message problems. Remove_Log_Writer_Route (event.Message.Route_From ()); Remove_Processing_Listener_Route (event.Message.Route_From ()); */ break; case NACK_CODE: //!!! Error handling or reporting? NACK_Messages++; break; default: Unrecognized_Messages++; break; } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (NL +"<-- Local_Theater.Assistant"); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<== Local_Theater.Assistant quit"); Assistant = null; } } // Assistant class. /*============================================================================== Processing_Listener */ /** The Processing_Listener interface implementation.

This method is used by the Conductor Management interface.

@param event The Conductor Management Processing_Event that has occurred. */ public void Processing_Event_Occurred ( Processing_Event event ) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (">>> Local_Theater.Processing_Event_Occurred:" + NL + event.Changes); Message message = Processing_Changes (PROCESSING_CHANGES_ACTION, event.Changes); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Message -" + NL + message); Vector remove_routes = new Vector (); int count; synchronized (Processing_Listener_Routes) { Value route; int index = Processing_Listener_Routes.size (); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + index + " Processing_Listener_Routes -"); Notify_Listener: while (--index >= 0) { route = Processing_Listener_Routes.get (index); count = remove_routes.size (); while (--count >= 0) if (route == remove_routes.get (count)) continue Notify_Listener; if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + route.Description ()); try {Send_Message (message.Route_To (route));} catch (Exception exception) { /* Either the Route_To Value is invalid (shouldn't happen) or the message could not be sent (IOException). In either case the route must be removed from the Processing_Listener_Routes list. */ remove_routes.add (route); } } } count = remove_routes.size (); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + count + " routes to remove -"); while (--count >= 0) Remove_Processing_Listener_Route (remove_routes.get (count)); remove_routes = null; if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Local_Theater.Processing_Event_Occurred"); } private void Add_Processing_Listener_Route ( Message_Delivered_Event event ) { Value route = event.Message.Route_From (); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (">>> Local_Theater.Add_Processing_Listener_Route: " + route.Description ()); boolean report = false; synchronized (Processing_Listener_Routes) { if (Processing_Listener_Routes.isEmpty ()) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Listener_Routes is empty"); Processing_Listener_Routes.add (route); /* Note that the Conductor will generate a Processing_Event that reports the current Conductor state. Since this is the first listener route it will be the only one to receive this report, as intended. */ The_Management.Add_Processing_Listener (this); } else { int index = Processing_Listener_Routes.size (); while (--index >= 0) { if ((Processing_Listener_Routes.get (index)).equals (route)) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Listener_Routes contains route" + NL +"<<< Local_Theater.Add_Processing_Listener_Route"); return; } } Processing_Listener_Routes.add (route); report = true; } } if (report) { // Send the current Conductor state Processing_Changes to the new listener. Message message = Processing_Changes (PROCESSING_CHANGES_ACTION, The_Management.State ()); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Changes report -" + NL + message); try {Send_Message (message.Reply_To (event.Message));} catch (Exception exception) { /* Either the Route_To Value is invalid (shouldn't happen) or the message could not be sent (IOException). In either case the route must be removed from the Processing_Listener_Routes list. */ Remove_Processing_Listener_Route (route); } } if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Local_Theater.Add_Processing_Listener_Route"); } private void Remove_Processing_Listener_Route ( Value route ) { if (route == null) return; if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Local_Theater.Remove_Processing_Listener_Route : " + route.Description ()); synchronized (Processing_Listener_Routes) { if (Processing_Listener_Routes.isEmpty ()) return; int index = Processing_Listener_Routes.size (); while (--index >= 0) { if ((Processing_Listener_Routes.get (index)).equals (route)) { Processing_Listener_Routes.remove (index); break; } } if (Processing_Listener_Routes.isEmpty ()) The_Management.Remove_Processing_Listener (this); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + Processing_Listener_Routes.size () + " Processing_Listener_Routes remaining" + NL +"<<< Local_Theater.Remove_Processing_Listener_Route"); } } /*============================================================================== Log_Writer */ private void Add_Log_Writer_Route ( Value route ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Local_Theater.Add_Log_Writer_Route: " + route.Description ()); if (Log_Writer.Is_Empty ()) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (" Log_Writer is empty"); Log_Writer.Add (new Messenger_Styled_Writer (Messenger (), route)); The_Management.Add_Log_Writer (Log_Writer); } else if (Log_Writer_Contains (route) == null) Log_Writer.Add (new Messenger_Styled_Writer (Messenger (), route)); if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (" " + Log_Writer.Writers ().size () + " Log_Writer entries" + NL +"<<< Local_Theater.Add_Log_Writer_Route"); } private boolean Remove_Log_Writer_Route ( Value route ) { if (route == null || Log_Writer.Is_Empty ()) return false; if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Local_Theater.Remove_Log_Writer_Route: " + route.Description ()); Suspendable_Styled_Writer writer = Log_Writer_Contains (route); if (writer != null) { Log_Writer.Remove (writer.Writer ()); if (Log_Writer.Is_Empty ()) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (" Log_Writer is empty"); The_Management.Remove_Log_Writer (Log_Writer); } } if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Local_Theater.Remove_Log_Writer_Route: " + (writer != null)); return writer != null; } private void Enable_Log_Writer_Route ( Value route, boolean enable ) { if (Log_Writer.Is_Empty ()) return; if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Local_Theater.Enable_Log_Writer_Route: " + (enable ? "En" : "Dis") + "able " + route.Description ()); Suspendable_Styled_Writer writer = Log_Writer_Contains (route); if (writer != null) Log_Writer.Suspend (writer.Writer (), enable); if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Local_Theater.Enable_Log_Writer_Route"); } private Suspendable_Styled_Writer Log_Writer_Contains ( Value route ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Local_Theater.Log_Writer_Contains: " + route.Description ()); synchronized (Log_Writer) { Vector writers = Log_Writer.Writers (); Suspendable_Styled_Writer writer; int index = writers.size (); while (--index >= 0) { writer = (Suspendable_Styled_Writer)writers.get (index); if (((Messenger_Styled_Writer)writer.Writer ()).Route ().equals (route)) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Local_Theater.Log_Writer_Contains: " + writer); return writer; } } } if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Local_Theater.Log_Writer_Contains: null"); return null; } } pirl-2.3.8/PIRL/Conductor/Maestro/Stage_Manager.java0000644000175000017500000005457011742733134022044 0ustar mathieumathieu/* Stage_Manager PIRL CVS ID: Stage_Manager.java,v 1.55 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Messenger.*; import PIRL.PVL.Parameter; import PIRL.PVL.PVL_Exception; import PIRL.Configuration.*; import PIRL.Utilities.UNIX_Process; import java.io.FileNotFoundException; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.Iterator; import java.util.StringTokenizer; /** A Stage_Manager is a Messenger manager for communication between Conductor processes and client processes.

@author Bradford Castalia - UA/PIRL @version 1.55 @see Theater @see Messenger */ public class Stage_Manager extends Dispatcher { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Stage_Manager (1.55 2012/04/16 06:04:12)"; /** The name used by the base Dispatcher class for the Stage_Manager service.

@see Dispatcher#Dispatcher_Name(String) */ public static final String STAGE_MANAGER_NAME = "Stage_Manager"; // Configuration parameters: /** The name of the Configuration parameter that specifies the Conductor command name.

If this parameter is not present or its value is empty or "disabled" remote Conductor command execution is disabled. */ public static final String CONDUCTOR_COMMAND_PARAMETER_NAME = "Conductor_Command_Name"; /** The name of the Configuration parameter that specifies the maximum number of remote Conductor commands that may be executed by a single request.

If the Count of remote commands exceeds this value no commands will be executed and an exception response will be sent in reply.

@see #CONDUCTOR_COMMAND_PARAMETER_NAME */ public static final String MAX_START_CONDUCTORS_COUNT_PARAMETER_NAME = "Max_Start_Conductors_Count"; /** The default maximum number of remote Conductor commands that may be executed by a single request.

@see #MAX_START_CONDUCTORS_COUNT_PARAMETER_NAME */ public static final int DEFAULT_MAX_START_CONDUCTORS_COUNT = 10; private int Max_Start_Conductors_Count = DEFAULT_MAX_START_CONDUCTORS_COUNT; // Message Actions and their parameters: public static final String START_CONDUCTORS_ACTION = "Start_Conductors", COUNT_PARAMETER_NAME = "Count", EXPLANATION_PARAMETER_NAME = Messenger.EXPLANATION_PARAMETER_NAME, COMMAND_PARAMETER_NAME = "Command", REPORT_PARAMETER_NAME = "Report", EXCEPTION_PARAMETER_NAME = Messenger.EXCEPTION_PARAMETER_NAME; private String Conductor_Command_Name = null; public static final String ASSISTANT_NAME = "Assistant"; private Assistant Assistant = null; private Start_Conductors_Assistant Start_Conductors_Assistant = null; private final BlockingQueue Tasks = new LinkedBlockingQueue (), Start_Conductors_Tasks = new LinkedBlockingQueue (); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_MESSAGES = 1 << 3, DEBUG_ASSISTANT = 1 << 4, DEBUG_START_CONDUCTORS = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Stage_Manager using a Configuration and port number.

The base {@link Dispatcher} class is constructed using the {@link #STAGE_MANAGER_NAME} along with the specified Configuration and port number. The {@link Dispatcher#Dispatcher_ID} is set to the {@link #ID} of this class.

@param configuration A Configuration. If null an attempt will be made to use a default configuration source. @param port The communications port number. If less than or equal to zero a {@link Dispatcher#DEFAULT_PORT} number will be used. */ public Stage_Manager ( Configuration configuration, int port ) { super (STAGE_MANAGER_NAME, configuration, port); Dispatcher_ID = ID; } /** Construct a Stage_Manager using a Configuration.

The base Dispatcher class will provide a default communications port number.

@param configuration A Configuration. If null an attempt will be made to use a default configuration source. @see #Stage_Manager(Configuration, int) */ public Stage_Manager ( Configuration configuration ) {this (configuration, 0);} /** Construct a Stage_Manager.

Defaults will be provided for the Configuration source and communications port number.

@see #Stage_Manager(Configuration, int) */ public Stage_Manager () {this (null, 0);} /*============================================================================== Configuration */ /** Configures the Stage_Manager.

The Configuration class is set to use this class when using a class-relative search for a configuration source when a Configuration is not provided.

The base Dispatcher class is {@link Dispatcher#Configure(Configuration) configured} and then Stage_Manager specific parameters are obtained.

{@link #CONDUCTOR_COMMAND_PARAMETER_NAME}
The name of the Conductor command used for remote execution requests. For best security this should be an absolute pathname to the executable file used to run a Conductor. If not present, empty or set to "DISABLED" remote commend execution is disabled.
{@link #MAX_START_CONDUCTORS_COUNT_PARAMETER_NAME}
The maximum number of command executions allowed for each request. This is used to prevent accidental hordes of Conductors from being run. The default value is {@link #DEFAULT_MAX_START_CONDUCTORS_COUNT}; the minimum value is 1.

N.B.: All parameters are sought in the {@link #Config_Pathname(String) in the Configuration Group} with the {@link Dispatcher#Dispatcher_Name() Stage_Manager name}.

@param configuration The Configuration to use. If null and defatult configuration source will sought. @throws Configuration_Exception If there was a problem parsing the configuration source, accessing its contents, or establishing a log file. @throws SecurityException If the connection authentication keys could not be generated, no {@link #PASSWORD_PARAMETER_NAME} was found but {@link Dispatcher#UNAUTHENTICATED_CONNECTIONS_ALLOWED} is false, or a log file could not be opened due to a security violation. */ protected void Configure ( Configuration configuration ) throws Configuration_Exception, SecurityException { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Stage_Manager.Configure"); Configuration.Relative_to_Class (Stage_Manager.class); super.Configure (configuration); Conductor_Command_Name = The_Configuration.Get_One (Config_Pathname (CONDUCTOR_COMMAND_PARAMETER_NAME)); if (Conductor_Command_Name != null && (Conductor_Command_Name.length () == 0 || Conductor_Command_Name.equalsIgnoreCase ("DISABLED"))) Conductor_Command_Name = null; Max_Start_Conductors_Count = (int)The_Configuration.Get_Number (Config_Pathname (MAX_START_CONDUCTORS_COUNT_PARAMETER_NAME), DEFAULT_MAX_START_CONDUCTORS_COUNT); if (Max_Start_Conductors_Count <= 0) Max_Start_Conductors_Count = 1; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Stage_Manager.Configure"); } /*============================================================================== Messages */ // Message_Delivered_Listener public synchronized void Message_Delivered ( Message_Delivered_Event event ) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Stage_Manager.Message_Delivered: " + event.Message.Action () + NL +" From -" + NL + (Messenger)event.getSource () + NL + ((Messenger)event.getSource ()).Identity () + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); try {Tasks.put (event);} catch (InterruptedException exception) {NACK ((Messenger)event.getSource (), event.Message);} if (Assistant == null) { Assistant = new Assistant (); Assistant.start (); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Stage_Manager.Message_Delivered"); } /*============================================================================== Assistants */ private class Assistant extends Thread { public Assistant () {super (STAGE_MANAGER_NAME + ' ' + ASSISTANT_NAME);} public void run () { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("==> Stage_Manager.Assistant"); Message_Delivered_Event event; Messenger messenger; String action; while (true) { try {event = Tasks.take ();} catch (InterruptedException exception) {break;} messenger = (Messenger)event.getSource (); action = event.Message.Action (); if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (NL +"--> Stage_Manager.Assistant: " + action + NL +" From -" + NL + messenger + NL + messenger.Identity () + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); if (START_CONDUCTORS_ACTION.equals (action)) { try {Start_Conductors_Tasks.put (event);} catch (InterruptedException exception) {NACK ((Messenger)event.getSource (), event.Message);} if (Start_Conductors_Assistant == null) { Start_Conductors_Assistant = new Start_Conductors_Assistant (); Start_Conductors_Assistant.start (); } } else Dispatch (event); if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (NL +"<-- Stage_Manager.Assistant"); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<== Stage_Manager.Assistant quit"); Log (NL + "The Stage_Manager Assistant quit." + NL); Assistant = null; } } // Assistant class. private void Dispatch ( Message_Delivered_Event event ) {super.Message_Delivered (event);} private class Start_Conductors_Assistant extends Thread { public Start_Conductors_Assistant () {super (STAGE_MANAGER_NAME + " Start_Conductors_" + ASSISTANT_NAME);} public void run () { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("==> Stage_Manager.Assistant"); Message_Delivered_Event event = null; while (true) { try {event = Start_Conductors_Tasks.take ();} catch (InterruptedException exception) {break;} Start_Conductors ((Messenger)event.getSource (), event.Message); } Log (NL + "The Stage_Manager Start_Conductors_Assistant quit." + NL); Start_Conductors_Assistant = null; } } // Start_Conductors_Assistant class. /*============================================================================== Message handlers */ private void Start_Conductors ( Messenger messenger, Message message ) { if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (">>> Stage_Manager.Start_Conductors:" + NL + messenger + NL + message.Routing () + NL + message); Message reply = Message.Action (START_CONDUCTORS_ACTION) .Reply_To (message); if (Conductor_Command_Name == null) { // Remote command execution disabled. Send (messenger, reply .Set (EXPLANATION_PARAMETER_NAME, "Remote command execution is not enabled.")); if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (" Remote command execution is not enabled." + NL +"<<< Stage_Manager.Start_Conductors"); return; } // Confirm a valid command name. String command_line = message.Get (COMMAND_PARAMETER_NAME); if (command_line == null || command_line.length () == 0) command_line = Conductor_Command_Name; else if (! command_line.equals (Conductor_Command_Name)) { // Invalid command name. Send (messenger, reply .Set (EXPLANATION_PARAMETER_NAME, "Invalid Conductor command name: " + command_line)); if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (" Invalid command name: " + command_line + NL +"<<< Stage_Manager.Start_Conductors"); return; } // Assemble the command line from the message parameters. int count = 1; String argument; boolean wait_to_start = false, manager = false; Parameter parameter; Iterator parameters = message.iterator (); while (parameters.hasNext ()) { // Option name. parameter = (Parameter)parameters.next (); argument = parameter.Name (); if (argument.length () == 0 || argument.equalsIgnoreCase (ACTION_PARAMETER_NAME) || argument.equals (COMMAND_PARAMETER_NAME)) continue; if (argument.equalsIgnoreCase (COUNT_PARAMETER_NAME)) { try {count = (int)parameter.Value ().long_Data ();} catch (PVL_Exception exception) {/* Ignore this parameter */} continue; } if (argument.charAt (0) != '-') // Add the option name marker. argument = "-" + argument; // Check for the -Wait_to_start option. if (argument.toUpperCase ().startsWith ("-W")) wait_to_start = true; command_line += " " + argument; if (parameter.Has_Value ()) { // Option value. try {argument = parameter.Value ().String_Data ();} catch (PVL_Exception exception) {/* Already checked */} if (argument.length () != 0) { // Check for a hidden -Manager option. StringTokenizer tokens = new StringTokenizer (argument); while (! manager && tokens.hasMoreTokens ()) if (tokens.nextToken ().toUpperCase ().startsWith ("-M")) manager = true; command_line += " " + argument; } } } String report; if (count <= 0) count = 1; else if (count > Max_Start_Conductors_Count) { String explanation = "Invalid Conductor command request."; report = "A Conductor command count of " + count + " exceeds the maximum of " + Max_Start_Conductors_Count + '.'; Log_Time (explanation + NL + report); Send (messenger, reply .Set (EXPLANATION_PARAMETER_NAME, explanation) .Set (EXCEPTION_PARAMETER_NAME, report)); if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (" " + explanation + NL +" " + report + NL +"<<< Stage_Manager.Start_Conductors"); return; } if (! wait_to_start && manager) // Add the wait-to-start option when a manager is also used. command_line += " -Wait_to_start"; reply.Set (COMMAND_PARAMETER_NAME, command_line); // Execute the command line. Log_Time ("Starting " + count + " Conductor" + ((count == 1) ? "" : "s") + " -" + NL + command_line); // Hold connection notifications in abeyance. boolean report_messenger_connections = Report_Messenger_Connections (); Report_Messenger_Connections (false); report = ""; int total = count; count = 0; while (count++ < total) { if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (" Starting Conductor " + count + " of " + total); String exception_identification = null; UNIX_Process procedure = null; try {procedure = new UNIX_Process (Runtime.getRuntime ().exec (command_line));} catch (IOException exception) { exception_identification = exception.toString (); report += NL + "Unable to execute Conductor command -" + NL + command_line + NL + exception_identification; } catch (NoSuchFieldException exception) { exception_identification = exception.toString (); report += NL + "The Process does not appear to be a UNIX Process." + NL + exception_identification; } catch (SecurityException exception) { exception_identification = exception.toString (); report += NL + "Unable to access the UNIX Process information." + NL + exception_identification; } if (exception_identification != null) { Send (messenger, reply .Set (EXPLANATION_PARAMETER_NAME, report) .Set (EXCEPTION_PARAMETER_NAME, exception_identification)); Log ("Started " + --count + " of " + total + " Conductor" + ((total == 1) ? "" : "s") + ':' + report + NL); report = null; // Abort Conductor startups. break; } // Check startup messages. String line = null, listing = ""; BufferedReader procedure_stdout = null; try { procedure_stdout = new BufferedReader ( new InputStreamReader ( procedure.getInputStream ())); while ((line = procedure_stdout.readLine ()) != null) { if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (line); if (line.equals (">>> READY <<<")) // Startup was successful. break; listing += line + NL; } } catch (IOException except) {} try { if (procedure_stdout != null) procedure_stdout.close (); } catch (IOException except) {} if (line == null) { // EOF from Conductor stdout. report += NL + "Conductor start in the " + Location () + " theater failed with command -" + NL + command_line; try { int status = procedure.waitFor (); report += NL + "Conductor " + procedure.ID () + " exited with status " + status + "."; } catch (InterruptedException exception) {} reply .Set (EXPLANATION_PARAMETER_NAME, report) .Set (EXCEPTION_PARAMETER_NAME, listing); Log ("Started " + --count + " of " + total + " Conductor" + ((total == 1) ? "" : "s") + ':' + report + NL + listing); if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println (" " + report + NL +" Conductor listing -" + NL + listing); report = null; // Abort Conductor startups. break; } else report += NL + "Started Conductor " + procedure.ID () + '.'; } if (report != null) { // All Conductors started. Log_Time ("Started " + total + " Conductor" + ((total == 1) ? "" : "s") + ':' + report); reply.Set (REPORT_PARAMETER_NAME, report); } Send (messenger, reply); if (report_messenger_connections) { Report_Messenger_Connections (report_messenger_connections); Report_Messengers (); } if ((DEBUG & DEBUG_START_CONDUCTORS) != 0) System.out.println ("<<< Stage_Manager.Start_Conductors"); } /*============================================================================== Application main */ public static void main ( String[] args ) { System.out.println (ID + NL); Default_Dispatcher_Name (STAGE_MANAGER_NAME); String configuration_filename = null, log_filename = null, name = ""; int port = 0; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].charAt (1)) { case 'C': // Configuration case 'c': if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') { System.out.println ("Missing configuration filename"); Usage (); } if (configuration_filename != null) { System.out.println ("Multiple configuration files -" + NL + configuration_filename + NL + "and" + NL + args[count]); Usage (); } configuration_filename = args[count]; break; case 'L': // Logging case 'l': if (++count == args.length || (args[count].length () > 1 && args[count].charAt (0) == '-')) { name = Default_Log_Filename (); --count; } else name = args[count]; if (log_filename != null && ! log_filename.equals (name)) { System.out.println ("Multiple log files -" + NL + " " + log_filename + NL + " and" + NL + " " + name); Usage (); } if (name.length () == 0) name = STDOUT; log_filename = name; break; case 'P': // Port case 'p': if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') Usage (); try {port = Integer.parseInt (args[count]);} catch (NumberFormatException exception) { System.out.println ("Invalid port number: " + args[count]); Usage (); } break; default: System.out.println ("Unknown argument: " + args[count]); case 'H': // Help case 'h': Usage (); } } else { System.out.println ("Unknown argument: " + args[count]); Usage (); } } Configuration configuration = null; if (configuration_filename != null) { try {configuration = new Configuration (configuration_filename);} catch (IllegalArgumentException exception) { System.out.println ("Unable to find the configuration file - " + configuration_filename); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (Configuration_Exception exception) { System.out.println ("Could not load the configuration file - " + configuration_filename + NL + exception.getMessage ()); System.exit (EXIT_CONFIG_FILE_PROBLEM); } } Stage_Manager stage_manager = new Stage_Manager (configuration, port); if (DEBUG != DEBUG_OFF) { if (log_filename == null) log_filename = STDOUT; } if (log_filename != null) { try {System.out.println (stage_manager.Log_Pathname (log_filename));} catch (FileNotFoundException exception) { System.out.println (exception.getMessage ()); System.exit (EXIT_NO_LOG_FILE); } stage_manager.Logging (true); } try {stage_manager.Start ();} catch (Configuration_Exception exception) { stage_manager.Log_Time ("Could not load the configuration." + NL + exception.getMessage () + NL); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (IOException exception) { stage_manager.Log_Time ("Server I/O failure:" + NL + exception.getMessage () + NL); System.exit (EXIT_IO_ERROR); } catch (SecurityException exception) { stage_manager.Log_Time ("Server security violation:" + NL + exception.getMessage () + NL); System.exit (EXIT_SECURITY_VIOLATION); } catch (Exception exception) { stage_manager.Log_Time ("Server failure:"); if (stage_manager.Logging ()) exception.printStackTrace (stage_manager.Log_Stream ()); stage_manager.Log (); System.exit (EXIT_UNKNOWN_EXCEPTION); } } public static void Usage () { System.out.println ("Usage: " + Default_Dispatcher_Name () + " " + NL +" Options -" + NL +" -Configuration " + " (default: " + Default_Configuration_Source () + ')' + NL +" -Logging []" + " (default: " + Default_Log_Filename () + ')' + NL +" -Port " + " (default: " + DEFAULT_PORT + ')' + NL +" -Help" + NL ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Conductor/Maestro/Theater.java0000644000175000017500000013517111742733134020740 0ustar mathieumathieu/* Theater PIRL CVS ID: Theater.java,v 1.46 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Processing_Changes; import PIRL.Messenger.*; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import PIRL.Configuration.Configuration_Exception; import PIRL.Utilities.Host; import java.io.IOException; import java.util.Hashtable; import java.util.Vector; import java.util.Iterator; /** A Theater provides basic Messenger connection and Message sending services with a Stage_Manager plus the Theater protocol facilities.

This class is designed as a base class for the Remote_Theater and Local_Theater classes that implement a network extended Management interface via Messenger communication with a Stage_Manager class - its clients use the Remote_Theater - that conveys Messages via a Messenger to and from a Conductor instance that use the Local_Theater. These subclasses provide a Message based protocol for transmitting all the Management information between a remote client application and local Conductor application. The common Message parameter names used by the protocol are defined here along with Message {@link Message#Action() action name} to {@link #Action_Code(String) action code} mapping for efficient Message {@link Message_Delivered_Listener#Message_Delivered(Message_Delivered_Event) delivery} handler switching.

This class can also be used as the primary interface for clients that will be working with the Stage_Manager directly rather than indirectly through the Management interface.

@author Bradford Castalia - UA/PIRL @version 1.46 @see Remote_Theater @see Local_Theater @see Stage_Manager */ public class Theater { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Theater (1.46 2012/04/16 06:04:12)"; public static final String THEATER_NAME = "Theater"; public static final String ACTION_PARAMETER_NAME = Message.ACTION_PARAMETER_NAME, // Basic actions: IDENTIFY_ACTION = Message.IDENTIFY_ACTION, KEY_PARAMETER_NAME = Stage_Manager.KEY_PARAMETER_NAME, IDENTITY_ACTION = Message.IDENTITY_ACTION, NACK_ACTION = Message.NACK_ACTION, UNDELIVERABLE_ACTION = Messenger.UNDELIVERABLE_ACTION, ORIGINAL_MESSAGE_PARAMETER_NAME = Messenger.ORIGINAL_MESSAGE_PARAMETER_NAME, DONE_ACTION = Message.DONE_ACTION, // Common parameters: NAME_PARAMETER_NAME = Message.NAME_PARAMETER_NAME, VALUE_PARAMETER_NAME = "Value", CLASS_ID_PARAMETER_NAME = Message.CLASS_ID_PARAMETER_NAME, EXPLANATION_PARAMETER_NAME = Messenger.EXPLANATION_PARAMETER_NAME, EXCEPTION_PARAMETER_NAME = Messenger.EXCEPTION_PARAMETER_NAME, THEATER_LOCATION_PARAMETER_NAME = "Theater_Location", // Stage_Manager actions: START_MESSENGER_REPORTING_ACTION = Stage_Manager.START_MESSENGER_REPORTING_ACTION, STOP_MESSENGER_REPORTING_ACTION = Stage_Manager.STOP_MESSENGER_REPORTING_ACTION, MESSENGERS_REPORT_ACTION = Stage_Manager.MESSENGERS_REPORT_ACTION, START_CONDUCTOR_ACTION = Stage_Manager.START_CONDUCTORS_ACTION, STATUS_REPORT_ACTION = Stage_Manager.STATUS_REPORT_ACTION, MESSENGER_STATUS_PARAMETER_NAME = Stage_Manager.MESSENGER_STATUS_PARAMETER_NAME, FORWARDING_MESSENGERS_PARAMETER_NAME = Messenger.FORWARDING_MESSENGERS_PARAMETER_NAME, MESSAGES_SENT_PARAMETER_NAME = Stage_Manager.MESSAGES_SENT_PARAMETER_NAME, MESSAGE_BYTES_SENT_PARAMETER_NAME = Stage_Manager.MESSAGE_BYTES_SENT_PARAMETER_NAME, MESSAGES_SENT_DROPPED_PARAMETER_NAME = Stage_Manager.MESSAGES_SENT_DROPPED_PARAMETER_NAME, MESSAGES_RECEIVED_PARAMETER_NAME = Stage_Manager.MESSAGES_RECEIVED_PARAMETER_NAME, MESSAGE_BYTES_RECEIVED_PARAMETER_NAME = Stage_Manager.MESSAGE_BYTES_RECEIVED_PARAMETER_NAME, MESSAGES_RECEIVED_DROPPED_PARAMETER_NAME = Stage_Manager.MESSAGES_RECEIVED_DROPPED_PARAMETER_NAME, MEMORY_STATUS_PARAMETER_NAME = Stage_Manager.MEMORY_STATUS_PARAMETER_NAME, MEMORY_AVAILABLE_PARAMETER_NAME = Stage_Manager.MEMORY_AVAILABLE_PARAMETER_NAME, MEMORY_ALLOCATED_PARAMETER_NAME = Stage_Manager.MEMORY_ALLOCATED_PARAMETER_NAME, MEMORY_FREE_PARAMETER_NAME = Stage_Manager.MEMORY_FREE_PARAMETER_NAME, // Management actions: CONDUCTOR_CONNECT_ACTION = Stage_Manager.LINK_MESSENGER_ACTION, CONDUCTOR_DISCONNECT_ACTION = Stage_Manager.UNLINK_MESSENGER_ACTION, ADDRESS_PARAMETER_NAME = Messenger.ADDRESS_PARAMETER_NAME, ROUTE_TO_PARAMETER_NAME = Stage_Manager.ROUTE_TO_PARAMETER_NAME, START_ACTION = "Start", PROCESSING_STATE_ACTION = "Processing_State", STOP_ACTION = "Stop", QUIT_ACTION = "Quit", CONFIGURATION_ACTION = "Config_Report", CONFIGURATION_PARAMETER_NAME = "Configuration", SOURCES_ACTION = "Sources", PROCEDURES_ACTION = "Procedures", TABLE_PARAMETER_NAME = "Table", GET_POLL_INTERVAL_ACTION = "Get_Poll_Interval", SET_POLL_INTERVAL_ACTION = "Set_Poll_Interval", GET_RESOLVER_DEFAULT_VALUE_ACTION = "Get_Resolver_Default_Value", SET_RESOLVER_DEFAULT_VALUE_ACTION = "Set_Resolver_Default_Value", GET_STOP_ON_FAILURE_ACTION = "Get_Stop_on_Failure", SET_STOP_ON_FAILURE_ACTION = "Set_Stop_on_Failure", SEQUENTIAL_FAILURES_ACTION = "Sequential_Failures", RESET_SEQUENTIAL_FAILURES_ACTION = "Reset_Sequential_Failures", PROCESSING_EXCEPTION_ACTION = "Processing_Exception", CONDUCTOR_STATE_ACTION = "Conductor_State", SOURCE_RECORD_PARAMETER_NAME = "Source_Record", PROCEDURE_RECORD_PARAMETER_NAME = "Procedure_Record", SOURCES_REFRESHED_PARAMETER_NAME = "Sources_Refreshed", PROCEDURES_CHANGED_PARAMETER_NAME = "Procedures_Changed", ADD_PROCESSING_LISTENER_ACTION = "Add_Processing_Listener", REMOVE_PROCESSING_LISTENER_ACTION = "Remove_Processing_Listener", PROCESSING_CHANGES_ACTION = "Processing_Changes", LOG_WRITER_ACTION = Messenger_Styled_Writer.WRITE_ACTION, LOG_WRITTEN_PARAMETER_NAME = Messenger_Styled_Writer.WRITTEN_PARAMETER_NAME, LOG_STYLE_PARAMETER_GROUP = Messenger_Styled_Writer.STYLE_PARAMETER_GROUP, ADD_LOG_WRITER_ACTION = "Add_Log_Writer", REMOVE_LOG_WRITER_ACTION = "Remove_Log_Writer", ENABLE_LOG_WRITER_ACTION = "Enable_Log_Writer"; public static final int // Management Protocol actions: MINIMUM_PROTOCOL_CODE = 100, IDENTIFY_CODE = 100, CONDUCTOR_CONNECT_CODE = 101, IDENTITY_CODE = 102, START_CODE = 103, PROCESSING_STATE_CODE = 104, STOP_CODE = 105, QUIT_CODE = 106, CONFIGURATION_CODE = 107, SOURCES_CODE = 108, PROCEDURES_CODE = 109, GET_POLL_INTERVAL_CODE = 110, SET_POLL_INTERVAL_CODE = 111, GET_RESOLVER_DEFAULT_VALUE_CODE = 112, SET_RESOLVER_DEFAULT_VALUE_CODE = 113, GET_STOP_ON_FAILURE_CODE = 114, SET_STOP_ON_FAILURE_CODE = 115, SEQUENTIAL_FAILURES_CODE = 116, RESET_SEQUENTIAL_FAILURES_CODE = 117, PROCESSING_EXCEPTION_CODE = 118, CONDUCTOR_STATE_CODE = 119, ADD_PROCESSING_LISTENER_CODE = 120, REMOVE_PROCESSING_LISTENER_CODE = 121, PROCESSING_CHANGES_CODE = 122, LOG_WRITER_CODE = 123, ADD_LOG_WRITER_CODE = 124, REMOVE_LOG_WRITER_CODE = 125, ENABLE_LOG_WRITER_CODE = 126, CONDUCTOR_DISCONNECT_CODE = 127, // Non-Management protocol actions: DONE_CODE = 0, NACK_CODE = 1, UNDELIVERABLE_CODE = 2, START_MESSENGER_REPORTING_CODE = 3, STOP_MESSENGER_REPORTING_CODE = 4, MESSENGERS_REPORT_CODE = 5, START_CONDUCTOR_CODE = 6, STATUS_REPORT_CODE = 7; private static Hashtable Action_Codes = new Hashtable (); static { Action_Codes.put (IDENTIFY_ACTION, new Integer (IDENTIFY_CODE)); Action_Codes.put (IDENTITY_ACTION, new Integer (IDENTITY_CODE)); Action_Codes.put (CONDUCTOR_CONNECT_ACTION, new Integer (CONDUCTOR_CONNECT_CODE)); Action_Codes.put (START_ACTION, new Integer (START_CODE)); Action_Codes.put (PROCESSING_STATE_ACTION, new Integer (PROCESSING_STATE_CODE)); Action_Codes.put (STOP_ACTION, new Integer (STOP_CODE)); Action_Codes.put (QUIT_ACTION, new Integer (QUIT_CODE)); Action_Codes.put (CONFIGURATION_ACTION, new Integer (CONFIGURATION_CODE)); Action_Codes.put (SOURCES_ACTION, new Integer (SOURCES_CODE)); Action_Codes.put (PROCEDURES_ACTION, new Integer (PROCEDURES_CODE)); Action_Codes.put (GET_POLL_INTERVAL_ACTION, new Integer (GET_POLL_INTERVAL_CODE)); Action_Codes.put (SET_POLL_INTERVAL_ACTION, new Integer (SET_POLL_INTERVAL_CODE)); Action_Codes.put (GET_RESOLVER_DEFAULT_VALUE_ACTION, new Integer (GET_RESOLVER_DEFAULT_VALUE_CODE)); Action_Codes.put (SET_RESOLVER_DEFAULT_VALUE_ACTION, new Integer (SET_RESOLVER_DEFAULT_VALUE_CODE)); Action_Codes.put (GET_STOP_ON_FAILURE_ACTION, new Integer (GET_STOP_ON_FAILURE_CODE)); Action_Codes.put (SET_STOP_ON_FAILURE_ACTION, new Integer (SET_STOP_ON_FAILURE_CODE)); Action_Codes.put (SEQUENTIAL_FAILURES_ACTION, new Integer (SEQUENTIAL_FAILURES_CODE)); Action_Codes.put (RESET_SEQUENTIAL_FAILURES_ACTION, new Integer (RESET_SEQUENTIAL_FAILURES_CODE)); Action_Codes.put (PROCESSING_EXCEPTION_ACTION, new Integer (PROCESSING_EXCEPTION_CODE)); Action_Codes.put (CONDUCTOR_STATE_ACTION, new Integer (CONDUCTOR_STATE_CODE)); Action_Codes.put (ADD_PROCESSING_LISTENER_ACTION, new Integer (ADD_PROCESSING_LISTENER_CODE)); Action_Codes.put (REMOVE_PROCESSING_LISTENER_ACTION, new Integer (REMOVE_PROCESSING_LISTENER_CODE)); Action_Codes.put (PROCESSING_CHANGES_ACTION, new Integer (PROCESSING_CHANGES_CODE)); Action_Codes.put (LOG_WRITER_ACTION, new Integer (LOG_WRITER_CODE)); Action_Codes.put (ADD_LOG_WRITER_ACTION, new Integer (ADD_LOG_WRITER_CODE)); Action_Codes.put (REMOVE_LOG_WRITER_ACTION, new Integer (REMOVE_LOG_WRITER_CODE)); Action_Codes.put (ENABLE_LOG_WRITER_ACTION, new Integer (ENABLE_LOG_WRITER_CODE)); Action_Codes.put (CONDUCTOR_DISCONNECT_ACTION, new Integer (CONDUCTOR_DISCONNECT_CODE)); Action_Codes.put (DONE_ACTION, new Integer (DONE_CODE)); Action_Codes.put (NACK_ACTION, new Integer (NACK_CODE)); Action_Codes.put (UNDELIVERABLE_ACTION, new Integer (UNDELIVERABLE_CODE)); Action_Codes.put (START_MESSENGER_REPORTING_ACTION, new Integer (START_MESSENGER_REPORTING_CODE)); Action_Codes.put (STOP_MESSENGER_REPORTING_ACTION, new Integer (STOP_MESSENGER_REPORTING_CODE)); Action_Codes.put (MESSENGERS_REPORT_ACTION, new Integer (MESSENGERS_REPORT_CODE)); Action_Codes.put (START_CONDUCTOR_ACTION, new Integer (START_CONDUCTOR_CODE)); Action_Codes.put (STATUS_REPORT_ACTION, new Integer (STATUS_REPORT_CODE)); } private static final Message THEATER_IDENTITY = Message .Identity (THEATER_NAME) .Set (CLASS_ID_PARAMETER_NAME, ID); private Messenger The_Messenger = null; private Message Stage_Manager_Identity = null; private boolean Opened = false; /** The port number delimiter character.

The delimiter, if present, occurs between the {@link #Host(String) hostname} and {@link #Port(String) port number} portions of a Theater {@link #Location(String) location} or {@link #Name(String) name}.

N.B.: The delimiter and trailing port number portion of a Theater location or name may not be present. In this case the port number is the {@link #Default_Port() default port}. */ public static final char PORT_DELIMITER_CHAR = ':'; /** The default communications port number.

The value is initialized to the {@link Stage_Manager#DEFAULT_PORT}. */ public static int Default_Port = Stage_Manager.DEFAULT_PORT; /** The default maximum amount of time, in seconds, a Theater will wait for a Message to be received.

@see #Default_Receive_Timeout(int) */ public static final int DEFAULT_RECEIVE_TIMEOUT = 10; /** The minimum amount of time, in seconds, a Theater will wait for a Message to be received.

@see #DEFAULT_RECEIVE_TIMEOUT */ public static final int MINIMUM_RECEIVE_TIMEOUT = 5; private static int Default_Receive_Timeout = DEFAULT_RECEIVE_TIMEOUT; private int Receive_Timeout = Default_Receive_Timeout; public static final String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 1, DEBUG_CONNECTION = 1 << 2, DEBUG_MESSAGES = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct an unopened Theater. */ public Theater () {} /** Construct and open a Theater.

@param host The name or IP address of the host system where the Stage_Manager is expected to be running. If null, "localhost" will be used. @param port The port number on the named host to use for establishing a communication channel to the Stage_Manager. If less than or equal to zero the {@link Stage_Manager#DEFAULT_PORT} will be used. @param identity An {@link Message#Identity(String) Identity} Message. N.B.: A {@link #KEY_PARAMETER_NAME} parameter may be required. If null an Identity using the {@link #THEATER_NAME} and no key parameter will be used. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there was a problem parsing any of the protocol messages. @see #Open(String, int, Message) */ public Theater ( String host, int port, Message identity ) throws IOException { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Theater"); Open (host, port, identity); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Theater"); } /** Construct and open a Theater connected to a Stage_Manager on the local host at the default port.

@param identity An {@link Message#Identity(String) Identity} Message. N.B.: A {@link #KEY_PARAMETER_NAME} parameter may be required. If null an Identity using the {@link #THEATER_NAME} and no key parameter will be used. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there was a problem parsing any of the protocol messages. @see #Open(String, int, Message) */ public Theater ( Message identity ) throws IOException {this (null, 0, identity);} /*============================================================================== Accessors */ /** Get the Messenger used for communication.

@return A Messenger. This will be null if the communication channel has not yet been {@link #Open(String, int, Message) opened}. */ public Messenger Messenger () {return The_Messenger;} /** Get the default communications port number.

@return The default communications port number. @see #Default_Port(int) */ public static int Default_Port () {return Default_Port;} /** Set the default communications port number.

This port number will be used when an attempt is made to {@link #Open(String, int, Message) open} the Theater using a port number that is not positive.

@param port The default communications port number. If not positive the {@link Stage_Manager#DEFAULT_PORT} is used. */ public static void Default_Port ( int port ) { if (port <= 0) port = Stage_Manager.DEFAULT_PORT; Default_Port = port; } /** Get the default maximum amount of time, in seconds, a Theater will wait for a Message to be received.

@return The time, in seconds, when a timeout will occur while waiting for a Message to be received. @see #Receive_Timeout() */ public static int Default_Receive_Timeout () {return Default_Receive_Timeout;} /** Set the default maximum amount of time, in seconds, a Theater will wait for a Message to be received.

N.B.: This value is only effective when a new Theater is constructed.

@param timeout The time, in seconds, when a timeout will occur while waiting for a Message to be received. The value will be limited to be greater than or equal to the {@link #MINIMUM_RECEIVE_TIMEOUT}. @see #Receive_Timeout() */ public static void Default_Receive_Timeout ( int timeout ) { if (timeout < MINIMUM_RECEIVE_TIMEOUT) timeout = MINIMUM_RECEIVE_TIMEOUT; Default_Receive_Timeout = timeout; } /** Get the maximum amount of time, in seconds, a Theater will wait for a Message to be received.

The value is initialized when a Theater is constructed to the {@link #Default_Receive_Timeout() default receive timeout}.

@return The time, in seconds, when a timeout will occur while waiting for a Message to be received. @see #Receive_Timeout(int) */ public int Receive_Timeout () {return Receive_Timeout;} /** Set the maximum amount of time, in seconds, a Theater will wait for a Message to be received.

@return The time, in seconds, when a timeout will occur while waiting for a Message to be received. The value will be limited to be greater than or equal to the {@link #MINIMUM_RECEIVE_TIMEOUT}. @see #Receive_Message() */ public Theater Receive_Timeout ( int timeout ) { if (timeout < MINIMUM_RECEIVE_TIMEOUT) timeout = MINIMUM_RECEIVE_TIMEOUT; Receive_Timeout = timeout; return this; } /** Get a protocol code for an action name.

@return A protocol code number. This will be -1 if the action name does not map to a protocol code. */ public static int Action_Code ( String action ) { Integer code = Action_Codes.get (action); if (code == null) return -1; return code.intValue (); } /** Get the Theater location.

The Theater location is the Theater's {@link #Messenger() Messenger} {@link Messenger#Client_Hostname() client hostname} with the {@link Messenger#Client_Port() client port number} following a colon (':') delimiter. This is the fully qualified location of a Theater.

@return The Theater location String. This will be null if no Messenger has yet been {@link #Open(String, int, Message) opened}. @see #Location(String) */ public String Location () { if (The_Messenger == null) return null; String location = The_Messenger.Client_Hostname (); if (location != null) location += ":" + The_Messenger.Client_Port (); return location; } /** Get the identity of the Theater's Stage_Manager.

N.B.: The availability of a Stage_Manager identity does not imply that the Theater is {@link #Opened() open}. The most recent Stage_Manager identity that was obtained when the Theater was last {@link #Open(String, int, Message) opened} is provided.

@return A Message containing the identity of the Stage_Mangager for the Theater. This will be null if the Theater has yet to be opened for the first time. */ public Message Stage_Manager_Identity () {return Stage_Manager_Identity;} /** Get the description of this Theater.

The first line of the description is the class {@link #ID}. The second line of the description provides the Theater location, if it is known, and whether or not the Theater is open. If the Theater has a {@link #Messenger() Messenger} its description is included on the following lines.

@return The Theater description String. */ public String toString () { String string = ID + NL + "The " + THEATER_NAME; String location = Location (); if (location != null) string += " at location " + location; string += " is " + (Opened ? "" : "not ") + "open."; if (The_Messenger != null) string += NL + The_Messenger; return string; } /*============================================================================== Stage_Manager Connection */ /** Open this Theater.

If this Theater is not {@link #Opened() opened} a new Messenger is constructed with a communication connection to the Stage_Manager at the port on the specifed host.

N.B.: The Messenger will that is constructed will be in synchronous message receive mode. The initial Stage_Manager handshake will be done. The handshake is initiated by an {@link #IDENTIFY_ACTION} Message {@link #Receive_Message() received} from the Stage_Manager with an {@link Message#Identity(String) Identity} Message sent in reply. : Unless the Stage_Manager will accept unauthenticated connections a {@link #KEY_PARAMETER_NAME} parameter with the required authentication value must be provided in which case the parameter of the same name from the Stage_Manager Identify Message will be used to encode the value from the Identity Message before it is sent.

To start asynchronously listening for messages set the {@link #Employer(Message_Delivered_Listener) employer} and begin to {@link #Listen_for_Messages() listen for messages}.

@param host The name or IP address of the host system where the Stage_Manager is expected to be running. If null, "localhost" will be used. @param port The port number on the named host to use for establishing a communication channel to the Stage_Manager. If less than or equal to zero the {@link #Default_Port()} will be used. @param identity An {@link Message#Identity(String) Identity} Message. N.B.: A {@link #KEY_PARAMETER_NAME} parameter may be required. If null an Identity using the {@link #THEATER_NAME} and no key parameter will be used. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there was a problem parsing any of the protocol messages. */ public void Open ( String host, int port, Message identity ) throws IOException { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Theater.Open: host " + host + " port " + port); if (Opened ()) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Already opened" + NL +"<<< Theater.Open"); return; } if (host == null) host = "localhost"; if (port <= 0) port = Default_Port; if (identity == null) identity = THEATER_IDENTITY; // Copy the identity Message so it can be modified. Message client_identity = null; try {client_identity = new Message (identity) .Set (KEY_PARAMETER_NAME, null);} catch (PVL_Exception exception) { throw new Theater_Protocol_Exception (ID + NL + "The identity message could not be parsed." + NL + exception.getMessage (), Theater_Protocol_Exception.INVALID_MESSAGE, exception); } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Constructing a Messenger on host " + host + " port " + port + NL); The_Messenger = new Messenger (host, port); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" The_Messenger: " + The_Messenger + NL +" Identity -" + NL + client_identity); // Set the Messenger identity, sans key. The_Messenger.Identity (client_identity); // Stage_Manager handshake: if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Stage_Manager handshake ..."); // Set Opened true so messages can be received and sent. Opened = true; // Receive the Identify Message. Message identify = Receive_Message (); if (identify == null) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" No " + IDENTIFY_ACTION + " message was received after " + Receive_Timeout + " seconds"); Close (); throw new Theater_Protocol_Exception (ID + NL + "The Theater could not be opened" + NL + "because the connection handshake with the Stage_Manager failed." + NL + "No " + IDENTIFY_ACTION + " message was received after " + Receive_Timeout + " second" + ((Receive_Timeout == 1) ? "." : "s."), Theater_Protocol_Exception.TIMEOUT); } if (! IDENTIFY_ACTION.equals (identify.Action ())) { Close (); throw new Theater_Protocol_Exception (ID + NL + "The Theater could not be opened" + NL + "because the connection handshake with the Stage_Manager failed." + NL + "An " + IDENTIFY_ACTION + " message was expected but not received -" + NL + identify, Theater_Protocol_Exception.INVALID_MESSAGE); } // Send a copy of the Identity Message with key that will be encoded. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Identify message received." + NL +" Sending the identity message"); try {client_identity = new Message (identity);} catch (PVL_Exception exception) {/* Already copied above */} Send_Message (Stage_Manager.Authentication (identify, client_identity) .Set (ACTION_PARAMETER_NAME, IDENTITY_ACTION) // Just to be sure. .Reply_To (identify)); // Receive the Stage_Manager response. identify = Receive_Message (); if (identify == null) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" No " + IDENTITY_ACTION + " message was received after " + Receive_Timeout + " seconds"); Close (); throw new Theater_Protocol_Exception (ID + NL + "The Theater could not be opened" + NL + "because the connection handshake with the Stage_Manager failed." + NL + "No response to the " + IDENTITY_ACTION + " message was received after " + Receive_Timeout + " second" + ((Receive_Timeout == 1) ? "." : "s."), Theater_Protocol_Exception.TIMEOUT); } if (NACK_ACTION.equals (identify.Action ())) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" NACK message received -" + NL + identify); Close (); String explanation = identify.Get (EXPLANATION_PARAMETER_NAME); throw new Theater_Protocol_Exception (ID + NL + "The Theater could not be opened" + NL + "because the connection handshake with the Stage_Manager failed." + NL + "The identity information was rejected." + ((explanation == null) ? "" : (NL + explanation)), Theater_Protocol_Exception.UNAUTHENTICATED); } Stage_Manager_Identity = identify; if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Stage_Manager identity received -" + NL + Stage_Manager_Identity + NL +"<<< Theater.Open"); } /** Open this Theater.

The {@link #Default_Port() default port} on the "localhost" will be used to open the communication connection.

@param identity An {@link Message#Identity(String) Identity} Message. N.B.: A {@link #KEY_PARAMETER_NAME} parameter may be required. If null an Identity using the {@link #THEATER_NAME} and no key parameter will be used. */ public void Open ( Message identity ) throws IOException {Open (null, 0, identity);} /** Test if this Theater is opened.

@return true if a Messenger is active and has a connected communication channel to the Stage_Manager; false otherwise. */ public boolean Opened () { if (Opened && The_Messenger != null && ! The_Messenger.Is_Connected ()) Opened = false; if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">-< Theater.Opened: " + Opened); return Opened; } /** Set the {@link Messenger#Employer(Message_Delivered_Listener) Messenger employer} to receive asynchronous message delivery.

@param employer A Message_Delivered_Listener employer object. If null, nothing is done. @return This Theater object. @throws IllegalStateException If not yet {@link #Open(String, int, Message) opened}. @see Message_Delivered_Listener */ protected Theater Employer ( Message_Delivered_Listener employer ) { if (employer != null) { if (The_Messenger == null) throw new IllegalStateException (ID + NL + "Can't set the employer until opened."); The_Messenger.Employer (employer); } return this; } /** Begin asynchronously listening for messages.

The Messenger is told to {@link Messenger#Listen_for_Messages() listen for messages}.

@return true if the Messenger began asynchronously listening for messages; false if the Messenger is not connected to the communication channel. @throws IllegalStateException If not yet {@link #Open(String, int, Message) opened}. */ public boolean Listen_for_Messages () { if (The_Messenger == null) throw new IllegalStateException (ID + NL + "Can't listen for messages until opened."); return The_Messenger.Listen_for_Messages () != null; } /** Close this Theater.

If this Theater is {@link #Opened() open} it's Messenger is given the {@link Messenger#Done(String) Done} message which will close the communication connection.

@return true if this Theater was open at the time the method was called; false if the Theater was already closed. */ public boolean Close () { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Theater.Close"); boolean closed = false; if (Opened ()) { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Closing"); /* The Opened flag is set false *before* the Done signal is sent to The_Messenger to prevent a Close loop in response to the Done Message received as a result of the Done signal. When the Done Message is received and Close is called the call to Opened will return false. */ Opened = false; The_Messenger.Done (ID + NL + "Close requested."); closed = true; } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Theater.Close: " + closed); return closed; } /*============================================================================== Messages */ /** Send a Message.

If not {@link #Opened() opened} or the Message is null nothing is done.

A Message is sent via the {@link #Messenger() Messenger}. If an IOException occurs the Stage_Manager is (@link #Close() closed}. However, if the message can not be parsed the Stage_Manager is not closed.

@param message A Message. If null nothing is done. @throws IOException If there was a problem send the message. This will be a Theater_Protocol_Exception if the message could not be parsed. */ public void Send_Message ( Message message ) throws IOException { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Theater.Send_Message"); if (! Opened () || message == null) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Theater.Send_Message: " + ((message == null) ? "No message" : "NOT CONNECTED")); return; } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (message.Routing () + NL +message); try {The_Messenger.Send (message);} catch (IOException exception) { Close (); throw exception; } catch (PVL_Exception exception) { throw new Theater_Protocol_Exception (ID + NL + "The message to be sent could not be parsed." + NL + exception.getMessage (), Theater_Protocol_Exception.INVALID_MESSAGE, exception); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Theater.Send_Message"); } /** Receive a Message.

If not {@link #Opened() opened} nothing is done and null is returned.

A Message is synchronously received from the {@link #Messenger() Messenger}. If the {@link #Receive_Timeout(int) receive timeout} expires null is returned. If an IOException occurs the Stage_Manager is (@link #Close() closed}. However, if the message can not be parsed the Stage_Manager is not closed.

@return A Message. This will be null if this Theater is not open or if the receive timeout occured. @throws IOException If there was a problem receiving a message. This will be a Theater_Protocol_Exception if the message could not be parsed. */ public Message Receive_Message () throws IOException { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Theater.Receive_Message"); if (! Opened ()) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Theater.Receive_Message: NOT CONNECTED"); return null; } Message message = null; try {message = The_Messenger.Receive (Receive_Timeout);} catch (IOException exception) { Close (); throw exception; } catch (PVL_Exception exception) { throw new Theater_Protocol_Exception (ID + NL + "The message received could not be parsed." + NL + exception.getMessage (), Theater_Protocol_Exception.INVALID_MESSAGE, exception); } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (" " + ((message == null) ? "No message recieved" : ("Message received -" + NL + message.Routing () + NL + message)) + NL +"<<< Theater.Receive_Message"); return message; } /*============================================================================== Utilities */ /** Provide an abbreviated theater location for a given theater location.

The format of a theater location string is:

hostname[delimiterport]

The hostname portion is the {@link #Name(String) name} obtained from the {@link #Host(String) host} part of the given location.

The delimiter is the {@link #PORT_DELIMITER_CHAR}. If it is present in the given location the remaining port portion is compared to the Theater's {@link #Default_Port() default port}. If they are the same the delimiter and port portion are not included in the returned location String.

@param location A theater location or name String. If null, or the emtpy String, null is returned. @return A possibly modified theater location String. @see #Location() */ public static String Location ( String location ) { if (location == null || location.length () == 0) return null; String hostname = Name (Host (location)), port = null; int index = location.indexOf (PORT_DELIMITER_CHAR); if (index >= 0) { port = location.substring (index); if (! port.equals (String.valueOf (PORT_DELIMITER_CHAR) + String.valueOf (Default_Port))) hostname += port; } return hostname; } /** Provide a fully qualified theater location.

The format of a theater location string is:

host[delimiterport]

The host may be a hostname, short or fully qualified, or IP address. If the hostname can be {@link Host#Full_Hostname(String) fully qualified} that name is used; otherwise the hostname portion of the location is used as given.

N.B.: A hashmap of host names to fully qualified hostnames is maintained to avoid the, often very time consuming, DNS lookup of canonical hostnames each time this utility is called on a previously examined location.

The optional port is the system port number used to connect to the Stage_Manager. If no port number is present in the given location the {@link #Default_Port() default port} is used. The delimiter is the {@link #PORT_DELIMITER_CHAR}.

@param location A theater location or name String. If null, or the emtpy String, null is returned. @return A possibly modified theater location String. @see #Location(String) */ public static String Full_Location ( String location ) { if (location == null || location.length () == 0) return null; String hostname, port = null; int index = location.indexOf (PORT_DELIMITER_CHAR); if (index >= 0) { if (index == 0 || index == (location.length () - 1)) // Port delimiter at the begining or end of the string. return location; hostname = location.substring (0, index); port = location.substring (++index); if (port.length () == 0) port = null; } else hostname = location; // Check the hostnames map for a cached reference. location = Hostnames_Map.get (hostname); if (location == null) { // Get the canonical hostname from the system. if ((location = Host.Full_Hostname (hostname)) == null) // Unknown; use the unchanged hostname. location = hostname; // Update the hostnames cache. Hostnames_Map.put (hostname, location); } if (port == null) port = String.valueOf (Theater.Default_Port ()); location += PORT_DELIMITER_CHAR + port; return location; } private static Hashtable Hostnames_Map = new Hashtable (); /** Get the name portion of a Theater location.

The name portion of a Theater location is the leading substring up to, but not including, the first period character ('.') in the String plus the trailing substring starting with the last {@link #PORT_DELIMITER_CHAR}, if present. If no period character is found the entire location is returned. Thus the {@link #Port(String) port number}, if present, is retained but only the {@link Host#Hostname(String) short hostname} (but without doing the system name lookup) is returned.

However, if the name portion of the string is numeric, then the entire name is always returned since this is most likely an IP address.

@param location A Theater location String. If null, null is returned. @return The Theater name String. */ public static String Name ( String location ) { if (location != null) { int name_index = location.indexOf ('.'); if (name_index > 0) { String name = location.substring (0, name_index); // Test for an IP address. try { Integer.parseInt (name); // The location appears to be an IP address. return location; } catch (NumberFormatException exception) {} int port_index = location.lastIndexOf (PORT_DELIMITER_CHAR); if (port_index >= 0) location = name + location.substring (port_index); else location = name; } } return location; } /** Get the Theater host name for a location or name.

@param location A Theater location or name String. @return If the location String contains a {@link #PORT_DELIMITER_CHAR} the substring preceeding the delimiter is returned. Otherwise the entire String is returned. If the location is null, null is returned. @see #Location(String) */ public static String Host ( String location ) { if (location == null) return null; int index = location.indexOf (PORT_DELIMITER_CHAR); if (index >= 0) return location.substring (0, index); return location; } /** Get the Theater communications port number for a location.

@param location A Theater location String. If null -1 is returned. @return If the location String contains a port number following a {@link #PORT_DELIMITER_CHAR} that value is returned. Otherwise the {@link #Default_Port() default port} for a Theater is returned. @see #Location(String) */ public static int Port ( String location ) { if (location != null) { int index = location.indexOf (PORT_DELIMITER_CHAR); if (index >= 0 && index < (location.length () - 1)) { try {return Integer.parseInt (location.substring (++index));} catch (NumberFormatException exception) {} } else return Theater.Default_Port (); } return -1; } /** Generate a NACK - no acknowledge - Message with optional explanation and exception parameters.

A {@link Message#NACK() NACK action Message} is obtained. If the explanation is non-null and not empty an {@link #EXPLANATION_PARAMETER_NAME} parameter is set in the Message with the explanation. If the exception is non-null an {@link #EXCEPTION_PARAMETER_NAME} parameter is set in the Message with the exception's String description.

@param explanation An explanatory String. May be null. @param exception An Exception associated with the NACK. May be null. @return A NACK action Message. */ public static Message NACK ( String explanation, Exception exception ) { Message message = Message.NACK (); if (explanation != null && explanation.length () != 0) message.Set (EXPLANATION_PARAMETER_NAME, explanation); if (exception != null) message.Set (EXCEPTION_PARAMETER_NAME, exception.toString ()); return message; } /** Assemble a Message containing Processing_Changes parameters.

The Message will have an {@link #ACTION_PARAMETER_NAME} with the specified action value.

Each Processing_Changes state variable that has a value indicating a changed Conductor state is entered as a parameter in the assembled Message:

{@link #CONFIGURATION_PARAMETER_NAME}
A Parameter Group containing the current Conductor Configuration parameters. This will not be present if the changes {@link Processing_Changes#Configuration() Configuration} is null.
{@link #PROCESSING_STATE_ACTION}
The current Conductor processing state code. This not be present if the changes {@link Processing_Changes#Processing_State() processing state} is zero.
{@link #SOURCE_RECORD_PARAMETER_NAME}
The current Conductor source record as an Array of String Values. This will not be present if the changes {@link Processing_Changes#Source_Record() source record} is null.
{@link #PROCEDURE_RECORD_PARAMETER_NAME}
The current Conductor procedure record as an Array of String Values. This will not be present if the changes {@link Processing_Changes#Procedure_Record() procedure record} is null.
{@link #SOURCES_REFRESHED_PARAMETER_NAME}
A flag value of "true" indicating that the Conductor has just refreshed its cache of source records. This will not be present if the changes {@link Processing_Changes#Sources_Refreshed() sources refreshed} value is false.
{@link #PROCEDURES_CHANGED_PARAMETER_NAME}
A flag value of "true" indicating that the Conductor has just reloaded a modified procedures table. This will not be present if the changes {@link Processing_Changes#Procedures_Changed() procedures changed} value is false;
{@link #SEQUENTIAL_FAILURES_ACTION}
The current number of sequential source record processing failures that the Conductor encountered. This will not be present if the changes {@link Processing_Changes#Sequential_Failures() sequential failures} value is less than zero.
{@link #PROCESSING_EXCEPTION_ACTION}
A description of the last processing error that occured in the Conductor. This will not be present if the changes {@link Processing_Changes#Error_Condition() error condition} is null.
{@link #QUIT_ACTION}
A flag value of "true" indicating that the Conductor process is about to exit. This will not be present if the changes {@link Processing_Changes#Exiting() exiting} value is false.

@param action A String naming the value of the {@link #ACTION_PARAMETER_NAME} parameter of the Message. If null, {@link #PROCESSING_CHANGES_ACTION} will be used. @param changes A Processing_Changes object. If null, null is returned. @return A Message containing Processing_Changes parameters. This will be null if, and only if, the changes argument is null. */ public static Message Processing_Changes ( String action, Processing_Changes changes ) { if (changes == null) return null; if (action == null) action = PROCESSING_CHANGES_ACTION; Message message = Message.Action (action); if (changes.Configuration () != null) message.Add (CONFIGURATION_PARAMETER_NAME, changes.Configuration ()); if (changes.Processing_State () != 0) message.Set (PROCESSING_STATE_ACTION, changes.Processing_State ()); if (changes.Source_Record () != null) { try {message.Set (SOURCE_RECORD_PARAMETER_NAME, changes.Source_Record ());} catch (PVL_Exception exception) {/* Invalid source record (shouldn't happen) */} } if (changes.Procedure_Record () != null) { try {message.Set (PROCEDURE_RECORD_PARAMETER_NAME, changes.Procedure_Record ());} catch (PVL_Exception exception) {/* Invalid procedure record (shouldn't happen) */} } if (changes.Sources_Refreshed ()) message.Set (SOURCES_REFRESHED_PARAMETER_NAME, "true"); if (changes.Procedures_Changed ()) message.Set (PROCEDURES_CHANGED_PARAMETER_NAME, "true"); if (changes.Sequential_Failures () >= 0) message.Set (SEQUENTIAL_FAILURES_ACTION, changes.Sequential_Failures ()); if (changes.Error_Condition () != null) message.Set (PROCESSING_EXCEPTION_ACTION, changes.Error_Condition ()); if (changes.Exiting ()) message.Set (QUIT_ACTION, "true"); return message; } /** Assemble a Processing_Changes object using Message parameter values.

Each possible {@link #Processing_Changes(String, Processing_Changes) Processing_Changes Message} parameter is sought in the provided Message. For parameters that are found their values are used to set the corresponding Conductor processing state variable in a Processing_Changes object that is initialized with all its variables set to the unchanged value.

@param message A Message containing Processing_Changes parameters. If null, null is returned. @return A Processing_Changes object. This will be null if, and only if, the message argument is null. */ public static Processing_Changes Processing_Changes ( Message message ) { if (message == null) return null; Processing_Changes changes = new Processing_Changes (); String value; try {changes.Configuration (message.Find (CONFIGURATION_PARAMETER_NAME, Parameter.AGGREGATE));} catch (Configuration_Exception exception) {/* Protocol error */} value = message.Get (PROCESSING_STATE_ACTION); if (value != null) { try {changes.Processing_State (Integer.parseInt (value));} catch (NumberFormatException exception) {/* Protocol error */} } value = message.Get (SEQUENTIAL_FAILURES_ACTION); if (value != null) { try {changes.Sequential_Failures (Integer.parseInt (value));} catch (NumberFormatException exception) {/* Protocol error */} } changes .Source_Record (Record (message.Value_of (SOURCE_RECORD_PARAMETER_NAME))) .Procedure_Record (Record (message.Value_of (PROCEDURE_RECORD_PARAMETER_NAME))) .Error_Condition (message.Get (PROCESSING_EXCEPTION_ACTION)) .Sources_Refreshed ("true".equals (message.Get (SOURCES_REFRESHED_PARAMETER_NAME))) .Procedures_Changed ("true".equals (message.Get (PROCEDURES_CHANGED_PARAMETER_NAME))) .Exiting ("true".equals (message.Get (QUIT_ACTION))); return changes; } /** Assemble a data table from an Array Value.

Each Array entry is expected to an Array Value that is assembled into a Record Vector.

@param array An Array of Array Values. @return A Vector of String Vectors. This will be null if any of the Array entries can not be assembled into a Record Vector of Strings. */ public static Vector> Table ( Value array ) { if (array == null || array.Array_Size () == 0) return null; Vector> table = new Vector> (array.Array_Size ()); Vector record; Iterator records = array.iterator (); while (records.hasNext ()) { if ((record = Record ((Value)records.next ())) == null) return null; table.add (record); } return table; } /** Assemble a data table record from an Array Value.

Each Array entry is converted into its String representation and added to the record Vector.

@param array A Array Value. @return A Vector of Strings. This will be null if any of the Array entries can not be converted to a String (for example, if the entry is an Array). */ public static Vector Record ( Value array ) { if (array == null || ! array.Is_Array ()) return null; Vector record = new Vector (array.Array_Size ()); Value field; Iterator fields = array.iterator (); while (fields.hasNext ()) { try {record.add (((Value)fields.next ()).String_Data ());} catch (PVL_Exception exception) { // The field is an Array! record = null; break; } } return record; } } pirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Definition.java0000644000175000017500000004667011742733133023460 0ustar mathieumathieu/* Conductor_Definition PIRL CVS ID: Conductor_Definition.java,v 1.14 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Conductor; import PIRL.Messenger.Message; import PIRL.PVL.Parameter; import PIRL.PVL.Parser; import PIRL.PVL.PVL_Exception; /** A Conductor_Definition contains the information that defines a Conductor instance.

A Conductor_Definition encapsulates the information necessary to instantiate a Conductor object.

Definition Name

The {@link #Name() name} of the definition is used to refer to the entire definition.Typically this name is expected to be unique in a set of Conductor_Definition objects such that if refers to a combination of definition parameter values that does not {@link #Matches(Message) match} the combination of parameter values in any other Conductor_Definition object of the set.

Definition Parameters

A Conductor_Defintion may contain the following parameters:

{@link #PIPELINE_PARAMETER_NAME}
The pipeline name used by a Conductor instantiation. This parameter is required. If it is not present when the Conductor_Definition is constructed the parameter is provided using the definition name for its value.
{@link #CONFIGURATION_PARAMETER_NAME}
The source of the Configuration used by a Conductor instantiation. This parameter is required. If it is not present, or the alternative when the Conductor_Definition is constructed the parameter is provided using the value of the {@link #CONFIGURATION_SOURCE_PARAMETER_NAME} or, if this is not present, the {@link #DEFAULT_CONFIGURATION_FILENAME} for its value.
{@link #SERVER_PARAMETER_NAME}
The name of the database server access parameters group in the configuration file used by the Conductor instantiation. This parameter is optional. If it is not present the Conductor will use the parameter of the same name to choose the default database server group from the Configuration.
{@link #CATALOG_PARAMETER_NAME}
The name of the catalog in the database server where the pipeline tables are located. This parameter is optional. If it is not present the Conductor will the parameter of the same name from the Configuration.

@author Michael Wendell and Bradford Castalia, UA/PIRL @version 1.14 @see Conductor */ public class Conductor_Definition extends Message { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Definition (1.14 2012/04/16 06:04:11)"; /** The name of the parameter that specifies the pipeline name. */ public static final String PIPELINE_PARAMETER_NAME = Conductor.PIPELINE_PARAMETER; /** The name of the parameter that specifies the Configuration source.

@see #CONFIGURATION_SOURCE_PARAMETER_NAME */ public static final String CONFIGURATION_PARAMETER_NAME = "Configuration"; /** The name of the parameter that specifies the Configuration source in a Conductor {@link Conductor#Identity()}.

@see #CONFIGURATION_PARAMETER_NAME */ public static final String CONFIGURATION_SOURCE_PARAMETER_NAME = Conductor.CONFIGURATION_SOURCE_PARAMETER; /** The default Configuration source filename. */ public static final String DEFAULT_CONFIGURATION_FILENAME = Conductor.DEFAULT_CONFIGURATION_FILENAME; /** The name of the parameter that specifies the database server access information parameters group name in the {@link #CONFIGURATION_PARAMETER_NAME Configuration source}. */ public static final String SERVER_PARAMETER_NAME = Conductor.DATABASE_SERVER_PARAMETER; /** The name of the parameter that specifies the database catalog containing the pipeline tables. */ public static final String CATALOG_PARAMETER_NAME = Conductor.CATALOG_PARAMETER; /** The name of the parameter that specifies the {@link #Start_Conductor_Message(boolean, int)} action. */ public static final String ACTION_PARAMETER_NAME = Message.ACTION_PARAMETER_NAME; /** The value of the {@link #ACTION_PARAMETER_NAME} parameter. */ public static final String START_CONDUCTORS_ACTION = Stage_Manager.START_CONDUCTORS_ACTION; /** The name of the parameter that is used to tell a Conductor instantiation to wait to start.

The Conductor will remain in the {@link Conductor#WAITING}.processing state if this parameter is included in the Conductor_Definition; otherwise it will immediately enter the {@link Conductor#RUNNING} processing state. */ public static final String WAIT_TO_START_PARAMETER_NAME = "Wait_to_Start"; /** The name of the parameter used to specify the number of Conductor instances to run in a Stage_Manger {@link #Start_Conductor_Message(boolean, int)}.

N.B.: This parameter is not a Conductor command line option. */ public static final String COUNT_PARAMETER_NAME = "Count"; private static final String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_MATCHES = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Conductor_Definition from a Message.

The Message parameters and name are copied and then the contents are {@link #Validate() validated}.

@param message A Message containg Conductor definition parameters. @throws PVL_Exception If the definition could not be copied into this object and {@link #Validate() validated}. @throws IllegalArgumentException If the Message content does not produce a {@link #Validate() valid} Conductor_Definition. */ public Conductor_Definition ( Message message ) throws PVL_Exception, IllegalArgumentException { super (message); Validate (); } /** Creates a Conductor_Definition from a Parameter.

If the Parameter is an Assignment it becomes the sole Parameter in this Message. If it is an Aggregate its Parameter list becomes the list of Parameters in this Message and its {@link #Name() name} becomes the name of this Message.

The contents of the new Conductor_Definition are {@link #Validate() validated}.

@throws PVL_Exception If the Parameter could not be copied into this object and {@link #Validate() validated}. @throws IllegalArgumentException If the Parameter content does not produce a {@link #Validate() valid} Conductor definition. */ public Conductor_Definition ( Parameter parameter ) throws PVL_Exception, IllegalArgumentException { super (parameter); Validate (); } /** Creates a Conductor_Definition from a pipeline name.

The resulting definition will have its {@link #Name() name} and {@link #PIPELINE_PARAMETER_NAME} parameter value set to the pipeline name. The {@link #CONFIGURATION_PARAMETER_NAME} will be set with the {@link #DEFAULT_CONFIGURATION_FILENAME} value. No other parameters will be present.

@param pipeline The name of the Conductor pipeline. @throws IllegalArgumentException If the pipeline name is null, empty or the special {@link Parser#CONTAINER_NAME}. */ public Conductor_Definition ( String pipeline ) throws IllegalArgumentException { if (pipeline == null) throw new IllegalArgumentException (ID + NL + "Can't construct from a null pipeline name."); if (pipeline.length () == 0 || pipeline.equals (Parser.CONTAINER_NAME)) throw new IllegalArgumentException (ID + NL + "Invalid \"" + pipeline + "\" pipeline name for constructor."); Name (pipeline); Set (PIPELINE_PARAMETER_NAME, pipeline); Set (CONFIGURATION_PARAMETER_NAME, DEFAULT_CONFIGURATION_FILENAME); } /** A Conductor_Definition can not be constructed without arguments. */ private Conductor_Definition () {} /*============================================================================== Accessors */ /** Test if a Conductor Definition Message matches this Conductor_Definition.

The match criteria are:

If the {@link #PIPELINE_PARAMETER_NAME} parameter is present in the message its value must match the value of the same parameter in this Conductor_Definition. If it is not present in the message the message {@link Message#Name() name} must match the parameter value in this Conductor_Definition. The Conductor_Definition will always contain this parameter; the Message must either contain the parameter with the same value or be named the same.

If the {@link #CONFIGURATION_PARAMETER_NAME} or {@link #CONFIGURATION_SOURCE_PARAMETER_NAME} parameter is present in the message its value must match the value of the same parameter in this Conductor_Definition. If it is not present in the message the parameter value in this Conductor_Definition must be {@link #DEFAULT_CONFIGURATION_FILENAME}. The Conductor_Definition will always contain this parameter; it is optional for the Message.

If the {@link #SERVER_PARAMETER_NAME} parameter is present in the message the same parameter must be present in this Conductor_Definition with the same value. If the parameter is not present in the message it must not be present in this Conductor_Definition. This parameter is optional for both the Conductor_Definition and the Message.

If the {@link #CATALOG_PARAMETER_NAME} parameter is present in the message the same parameter must be present in this Conductor_Definition with the same value. If the parameter is not present in the message it must not be present in this Conductor_Definition. This parameter is optional for both the Conductor_Definition and the Message.

N.B.: The {@link #Name() name} of the Message does not need to match the name of this Conductor_Definition. Also, the Message may contain additional parameters other than those used to determine the match; the match is only against the Conductor_Definition parameters.

@param message A Message. If null, false is returned. @return true if the Message contains a Conductor definition that logically matches this Conductor_Definition. */ public boolean Matches ( Message message ) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (">>> Conductor_Definition.Matches:" + NL +" this definition -" + NL + toString () + NL +" that message -" + NL + message); if (message == null) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println ("<<< Conductor_Definition.Matches: false"); return false; } String that_value; if ((that_value = message.Get (PIPELINE_PARAMETER_NAME)) == null) that_value = message.Name (); if (! that_value.equals (Get (PIPELINE_PARAMETER_NAME))) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + PIPELINE_PARAMETER_NAME + " " + Get (PIPELINE_PARAMETER_NAME) + " != " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } if ((that_value = message.Get (CONFIGURATION_PARAMETER_NAME)) == null && (that_value = message.Get (CONFIGURATION_SOURCE_PARAMETER_NAME)) == null) that_value = DEFAULT_CONFIGURATION_FILENAME; if (! that_value.equals (Get (CONFIGURATION_PARAMETER_NAME))) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + CONFIGURATION_PARAMETER_NAME + " " + Get (CONFIGURATION_PARAMETER_NAME) + " != " + ((message.Get (CONFIGURATION_PARAMETER_NAME) != null) ? CONFIGURATION_PARAMETER_NAME : CONFIGURATION_SOURCE_PARAMETER_NAME) + " " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } String this_value; this_value = Get (SERVER_PARAMETER_NAME); that_value = message.Get (SERVER_PARAMETER_NAME); if (that_value != null && ! that_value.equals (this_value)) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + SERVER_PARAMETER_NAME + " " + this_value + " != " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } else if (this_value != null && ! this_value.equals (that_value)) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + SERVER_PARAMETER_NAME + " " + this_value + " != " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } this_value = Get (CATALOG_PARAMETER_NAME); that_value = message.Get (CATALOG_PARAMETER_NAME); if (that_value != null && ! that_value.equals (this_value)) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + CATALOG_PARAMETER_NAME + " " + this_value + " != " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } else if (this_value != null && ! this_value.equals (that_value)) { if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println (" " + CATALOG_PARAMETER_NAME + " " + this_value + " != " + that_value + NL +"<<< Conductor_Definition.Matches: false"); return false; } if ((DEBUG & DEBUG_MATCHES) != 0) System.out.println ("<<< Conductor_Definition.Matches: true"); return true; } /** Generate a Message used by the Stage_Manager to instantiate a Conductor.

A {@link #Validate() validated} copy of this Conductor_Definition is filtered for use as a Stage_Manager Conductor start request Message.

The Conductor_Definition must contain a {@link #PIPELINE_PARAMETER_NAME} parameter. The new Message will have an {@link #ACTION_PARAMETER_NAME} set to {@link #START_CONDUCTORS_ACTION}.

@param wait_to_start If true a {@link #WAIT_TO_START_PARAMETER_NAME} token parameter will be included in the returned Message. @param count If the value of this integer is greater than 1 a {@link #COUNT_PARAMETER_NAME} parameter having the count value will be included in the returned Message. @return A Message suitable for use as a Stage_Manager Conductor start request message. @throws IllegalArgumentException If this Conductor_Description is not {@link #Validate() valid}. @throws PVL_Exception If this Conductor_Definition could not be copied into a new Message. */ public Message Start_Conductor_Message ( boolean wait_to_start, int count ) throws IllegalArgumentException, PVL_Exception { // Get a validated copy of this definition. Message message = new Conductor_Definition (this); message.Set (ACTION_PARAMETER_NAME, START_CONDUCTORS_ACTION, 0); if (wait_to_start) message.Set_Token (WAIT_TO_START_PARAMETER_NAME); if (count > 1) message.Set (COUNT_PARAMETER_NAME, count); return message; } /** Generate a Message used by the Stage_Manager to instantiate a Conductor.

This is a convenience interface to the {@link #Start_Conductor_Message(boolean, int)} method.

@param wait_to_start If true a {@link #WAIT_TO_START_PARAMETER_NAME} token parameter will be included in the returned Message. @param count If the value of this Integer is greater than 1 a {@link #COUNT_PARAMETER_NAME} parameter having the count value will be included in the returned Message. @return A Message suitable for use as a Stage_Manager Conductor start request message. @throws PVL_Exception If this Conductor_Definition could not be copied into a new Message. @throws IllegalArgumentException If this Conductor_Description does not contain a {@link #PIPELINE_PARAMETER_NAME} parameter. @see #Start_Conductor_Message(boolean, int) */ public Message Start_Conductor_Message ( boolean wait_to_start, Integer count ) throws PVL_Exception, IllegalArgumentException {return Start_Conductor_Message (wait_to_start, ((count == null) ? 1 : count.intValue ()));} /*============================================================================== Manipulators */ /** Validate this Conductor_Definition.

The values of all Conductor definition parameters are obtained and checked for definition rule violations:

Each Conductor_Definition parameter, if present, must have a non-empty value.

If the {@link #PIPELINE_PARAMETER_NAME} parameter is not present the {@link #Name() name} of the definition must not be the {@link Parser#CONTAINER_NAME} or empty. The default value for this required parameter is the name of the definition so the name must be valid if the parameter is not present.

All of the current parameters are then removed and the Conductor definition parameters are set to appropriate values:

If the {@link #PIPELINE_PARAMETER_NAME} parameter was not present it is set to the definition name; the default value for this required parameter is the name of the definition. If the parameter was present and the name of the defintion is the {@link Parser#CONTAINER_NAME} or empty the definition name is set to the value of the parameter. This parameter will be the first entry in the Conductor_Definition.

If neither the {@link #CONFIGURATION_PARAMETER_NAME} parameter nor {@link #CONFIGURATION_SOURCE_PARAMETER_NAME} parameter was present the {@link #CONFIGURATION_PARAMETER_NAME} parameter is set to the {@link #DEFAULT_CONFIGURATION_FILENAME}. This parameter will be the third entry in the Conductor_Definition.

If the {@link #SERVER_PARAMETER_NAME} and {@link #CATALOG_PARAMETER_NAME} parameters were present they are restored. They are not entered if they were not originally present.

N.B.: Any other parameters are not relevant to a Conductor_Definition and so are removed.

@throws IllegalArgumentException If the {@link #PIPELINE_PARAMETER_NAME} parameter isn't present and the {@link #Name() name} of the definition is the {@link Parser#CONTAINER_NAME}. */ public void Validate () throws IllegalArgumentException { String pipeline = Parameter_Value (PIPELINE_PARAMETER_NAME), configuration = Parameter_Value (CONFIGURATION_PARAMETER_NAME), server = Parameter_Value (SERVER_PARAMETER_NAME), catalog = Parameter_Value (CATALOG_PARAMETER_NAME); if (configuration == null) configuration = Parameter_Value (CONFIGURATION_SOURCE_PARAMETER_NAME); if (pipeline == null && (Name ().equals (Parser.CONTAINER_NAME) || Name ().length () == 0)) throw new IllegalArgumentException (ID + NL + "Invalid Conductor definition." + NL + "No definition name or \"" + PIPELINE_PARAMETER_NAME + "\" parameter -" + NL + Description ()); // Start with a clean slate. Remove_All (); if (pipeline == null) // The name will be valid from check above. pipeline = Name (); else if (Name ().equals (Parser.CONTAINER_NAME) || Name ().length () == 0) Name (pipeline); Set (PIPELINE_PARAMETER_NAME, pipeline); if (configuration == null) configuration = DEFAULT_CONFIGURATION_FILENAME; Set (CONFIGURATION_PARAMETER_NAME, configuration); if (server != null) Set (SERVER_PARAMETER_NAME, server); if (catalog != null) Set (CATALOG_PARAMETER_NAME, catalog); } private String Parameter_Value ( String parameter_name ) { String value = Get (parameter_name); if (value != null && value.length () == 0) throw new IllegalArgumentException (ID + NL + "Invalid Conductor definition." + NL + "Empty \"" + parameter_name + "\" parameter value -" + NL + Description ()); return value; } } pirl-2.3.8/PIRL/Conductor/Maestro/Remote_Theater.java0000644000175000017500000011153611742733133022251 0ustar mathieumathieu/* Remote_Theater PIRL CVS ID: Remote_Theater.java,v 1.36 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Management; import PIRL.Conductor.Processing_Listener; import PIRL.Conductor.Processing_Changes; import PIRL.Conductor.Processing_Event; import PIRL.Messenger.*; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; import PIRL.Strings.String_Buffer; import PIRL.Utilities.Styled_Multiwriter; import java.io.Writer; import java.io.IOException; import java.io.EOFException; import java.util.Vector; import java.util.Iterator; /** A Remote_Theater implements the remote Conductor management side of a {@link Theater}.

A Remote_Theater is used by network distributed clients that will be using the {@link Management} interface of Conductors via a Stage_Manager proxy server.

@author Bradford Castalia - UA/PIRL @version 1.36 @see Message_Delivered_Listener */ public class Remote_Theater extends Theater implements Management, Message_Delivered_Listener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Remote_Theater (1.36 2012/04/16 06:04:11)"; /** The name to be used in describing objects of this class. */ public static final String REMOTE_THEATER_NAME = "Remote_Theater"; private static final Message REMOTE_THEATER_IDENTITY; static { REMOTE_THEATER_IDENTITY = Message.Identity () .Set (NAME_PARAMETER_NAME, REMOTE_THEATER_NAME) .Set (CLASS_ID_PARAMETER_NAME, ID); } // Also used as the wait-response lock. private Message Conductor_Identity = new Message (); private String Authentication_Key = null; /** The default amount of time (seconds) to wait for a response to a protocol message. */ public static final int DEFAULT_RESPONSE_TIMEOUT = 10; private volatile int Response_Timeout = DEFAULT_RESPONSE_TIMEOUT; /** The default number of protocol transmission retries before a Remote_Management_Exception will be thrown. */ public static final int DEFAULT_MESSAGE_RETRIES = 3; private int Message_Retries = DEFAULT_MESSAGE_RETRIES; private Thread Waiting = null; private Message Response = null; private Vector Processing_Listeners = new Vector (); /** Object to which non-protocol Messages will be delivered. */ protected Message_Delivered_Listener Non_Protocol_Message_Listener = null; private Styled_Multiwriter Log_Writer = new Styled_Multiwriter (); /** Problem case message accounting. */ protected int Undeliverable_Messages = 0, NACK_Messages = 0, Unknown_Messages = 0; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONNECTION = 1 << 1, DEBUG_MESSAGES = 1 << 2, DEBUG_PROCESSING_LISTENER = 1 << 3, DEBUG_LOG_WRITER = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Remote_Theater ( String host, int port, Message identity, String connection_address ) throws IOException {Open (host, port, identity, connection_address);} public Remote_Theater ( String host, Message identity, String connection_address ) throws IOException {Open (host, 0, identity, connection_address);} public Remote_Theater () {} /*============================================================================== Accessors */ /** Get the amount of time to wait for a response to a protocol message.

@return The maximum amount of time (seconds) to wait for a response to a protocol message. */ public int Response_Timeout () {return Response_Timeout;} /** Set the amount of time to wait for a response to a protocol message.

@param seconds The maximum amount of time (seconds) to wait for a response to a protocol message. If less than or equal to zero the {@link #DEFAULT_RESPONSE_TIMEOUT} will be used. @return This Remote_Theater object. */ public Remote_Theater Response_Timeout ( int seconds ) { if (seconds <= 0) seconds = DEFAULT_RESPONSE_TIMEOUT; Response_Timeout = seconds; return this; } /** Get the identity of the Conductor with which this Remote_Theater is associated.

N.B.: The availability of a Conductor identity does not imply that this Remote_Theater is {@link Theater#Opened() opened} for that Conductor.

@return A {@link PIRL.Conductor.Conductor#Identity() Conductor identity Message}. This will be an empty Message if no identity has yet been obtained. It will be null if the identity has become corrupted. */ public Message Conductor_Identity () { Message identity = null; if (Conductor_Identity != null) { try {identity = new Message (Conductor_Identity);} catch (PVL_Exception exception) {} } return identity; } /** Get the maximum number of protocol message retries before a Remote_Management_Exception will be thrown.

@return The maximum number of protocol message retries. */ public int Message_Retries () {return Message_Retries;} /** Set the number of protocol message retries before a Remote_Management_Exception will be thrown.

@param retries The maximum number of protocol message retries. If zero the {@link #DEFAULT_MESSAGE_RETRIES} will be used. If negative retries will continue until successful (this could result in an indefintate protocol hang). @return This Remote_Theater object. */ public Remote_Theater Message_Retries ( int retries ) { if (retries == 0) retries = DEFAULT_MESSAGE_RETRIES; Message_Retries = retries; return this; } /** Get the number of undeliverable messages that have occurred.

An underliverable Message contains the {@link #UNDELIVERABLE_ACTION} value for the {@link #ACTION_PARAMETER_NAME}.

@return The number of undeliverable messages that have occurred. */ public int Undeliverable_Messages () {return Undeliverable_Messages;} /** Get the number of NACK (negative acknowledge) messages that have occurred.

A NACK Message contains the {@link #NACK_ACTION} value for the {@link #ACTION_PARAMETER_NAME}.

@return The number of NACK messages that have occurred. */ public int NACK_Messages () {return NACK_Messages;} /** Get the number of unknown messages that have occurred.

An unknown message has no {@link #ACTION_PARAMETER_NAME}, this parameter has no value, or the value is not known as a protocol action. A {@link Message_Delivered_Listener listener} may be set to receive non-protocol messages.

@return The number of undeliverable messages that have occurred. @see #Non_Protocol_Message_Listener(Message_Delivered_Listener) */ public int Unknown_Messages () {return Unknown_Messages;} /** Get the listener for non-protocol messages.

@return A Message_Delivered_Listener to which non-protocol messages will be delivered. This may be null. @see #Non_Protocol_Message_Listener(Message_Delivered_Listener) */ public Message_Delivered_Listener Non_Protocol_Message_Listener () { if (Non_Protocol_Message_Listener == null) return null; else { synchronized (Non_Protocol_Message_Listener) {return Non_Protocol_Message_Listener;} } } /** Set the listenter for non-protocol messages.

A non-protocol message does not participate in the implementation of the remote Conductor Management interface. This includes Messages with the {@link #DONE_ACTION}, {@link #UNDELIVERABLE_ACTION}, {@link #NACK_ACTION} or {@link #MESSENGERS_REPORT_ACTION} value for the {@link #ACTION_PARAMETER_NAME}, or any message that does not have a recognized protocol action.

@param listener A Message_Delivered_Listener to which non-protocol messages will be delivered. @return This Remote_Theater object. @see #Non_Protocol_Message_Listener() */ public Remote_Theater Non_Protocol_Message_Listener ( Message_Delivered_Listener listener ) { if (Non_Protocol_Message_Listener == null) Non_Protocol_Message_Listener = listener; else { synchronized (Non_Protocol_Message_Listener) {Non_Protocol_Message_Listener = listener;} } return this; } public String toString () { return ID + NL + super.toString (); } /*============================================================================== Stage_Manager */ /** Open this Theater.

The current communication channel is {@link #Close() closed}. A new {@link Theater#Open(String, int, Message) connection} with the Stage_Manager is established using the identity provided.

Then a {@link #CONDUCTOR_CONNECT_ACTION} Message is sent to the Stage_Manager with the connection ID. The response is checked for an {@link #EXPLANATION_PARAMETER_NAME} indicating that the request could not be satisfied (unknown Messenger ID). If the response is good then the {@link Message#Route_To() route-to list} of the Conductor Identity Message is set to the value provided by its {@link #ROUTE_TO_PARAMETER_NAME} parameter.

@param host The name or IP address of the host system where a Stage_Manager is running. If null, "localhost" will be used. @param port The port number to use to establish the communication connection. If less than or equal to zero the {@link Theater#Default_Port(int) default port} will be used. @param identity The Message used to provide the Stage_Manager with the required identity information. @param connection_address The Messenger {@link Messenger#Address() address} used by the Stage_Manager to identify the Messenger used to establish a connection with the Local_Theater object bound to a Conductor. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there were any problems in the content of the Messages used to establish the connection. @throws IllegalArgumentException If the identity or connection_address is null. @see Theater#Open(String, int, Message) */ public void Open ( String host, int port, Message identity, String connection_address ) throws IOException { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Remote_Theater.Open:" + NL +" Host - " + host + NL +" Port - " + port + NL +" Identity - " + NL + identity + NL +" Connection address - " + connection_address); if (identity == null) throw new IllegalArgumentException (ID + NL + "Can't open without an identity."); if (connection_address == null) throw new IllegalArgumentException (ID + NL + "Can't open without a Conductor connection address."); // Close if opened. Close (); // Connect to the Stage_Manager. Open (host, port, identity); // Request a Conductor connection. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Sending Conductor connection Message"); Send_Message (Message .Action (CONDUCTOR_CONNECT_ACTION) .Set (ADDRESS_PARAMETER_NAME, connection_address)); Message response = Receive_Message (); if (response == null || response.Get (EXPLANATION_PARAMETER_NAME) != null) { Close (); throw new Theater_Protocol_Exception (ID + NL + "Could not open because the Conductor connection failed." + NL + ((response == null) ? ("No response to the " + CONDUCTOR_CONNECT_ACTION + " request" + NL +"after " + Receive_Timeout () + " seconds.") : response.Get (EXPLANATION_PARAMETER_NAME)), ((response == null) ? Theater_Protocol_Exception.TIMEOUT : Theater_Protocol_Exception.INVALID_MESSAGE)); } // Validate the response message. Parameter parameter = response.Find (IDENTITY_ACTION); if (parameter == null) { Close (); throw new Theater_Protocol_Exception (ID + NL + "Could not open because the Conductor connection failed:" + NL + "No Conductor identity in the connection response -" + NL + response.Description (), Theater_Protocol_Exception.INVALID_MESSAGE); } try {Conductor_Identity = new Message (parameter);} catch (PVL_Exception exception) { Close (); throw new Theater_Protocol_Exception (ID + NL + "Could not open because the Conductor connection failed:" + NL + "Invalid Conductor identity -" + NL + parameter.Description (), Theater_Protocol_Exception.INVALID_MESSAGE); } parameter = Conductor_Identity.Find (ROUTE_TO_PARAMETER_NAME); if (parameter == null) { Close (); throw new Theater_Protocol_Exception (ID + NL + "Could not open because the Conductor connection failed:" + NL + "No " + ROUTE_TO_PARAMETER_NAME + " parameter in the Conductor identity -" + NL + Conductor_Identity, Theater_Protocol_Exception.INVALID_MESSAGE); } if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Conductor route-to: " + parameter.Description ()); // Set the route-to list of the identity message. try {Conductor_Identity.Route_To (parameter.Value ());} catch (PVL_Exception exception) { Close (); throw new Theater_Protocol_Exception (ID + NL + "Could not open because the Conductor connection failed:" + NL + "Invalid " + ROUTE_TO_PARAMETER_NAME + " parameter value in the Conductor identity -" + NL + Conductor_Identity, Theater_Protocol_Exception.INVALID_MESSAGE, exception); } String connected_address = Conductor_Identity.Get (ADDRESS_PARAMETER_NAME); if (connected_address == null) Conductor_Identity.Set (ADDRESS_PARAMETER_NAME, connection_address); else if (! connected_address.equals (connection_address)) { Conductor_Identity.Set (ADDRESS_PARAMETER_NAME, connection_address); Close (); throw new Theater_Protocol_Exception (ID + NL + "The opened theater's connection address - " + connected_address + NL + "is not the requested connection address - " + connection_address, Theater_Protocol_Exception.INVALID_MESSAGE); } // Start asychronous message listening. if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (" Starting asychronous message listening"); Employer (this); Listen_for_Messages (); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Remote_Theater.Open"); } /** Open this Theater.

The {@link Theater#Default_Port(int) default port} will be used.

@param host The name or IP address of the host system where a Stage_Manager is running. If null, "localhost" will be used. @param identity The Message used to provide the Stage_Manager with the required identity information. @param connection_address The Messenger {@link Messenger#Address() address} used by the Stage_Manager to identify the Messenger used to establish a connection with the Local_Theater object bound to a Conductor. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there were any problems in the content of the Messages used to establish the connection. @throws IllegalArgumentException If the identity or connection_address is null. @see #Open(String, int, Message, String) */ public void Open ( String host, Message identity, String connection_address ) throws IOException {Open (host, 0, identity, connection_address);} /** Close this Theater.

If currently {@link Theater#Opened() opened} a {@link Theater#CONDUCTOR_DISCONNECT_ACTION} Message is {@link Theater#Send_Message(Message) sent} with the {@link Theater#ADDRESS_PARAMETER_NAME} set from the same parameter of the {@link #Conductor_Identity() Conductor identity}. Then the communication channel is {@link Theater#Close() closed}.

@return true if this Remote_Theater was opened when this method was invoked; false if it was already closed. */ public synchronized boolean Close () { if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println (">>> Remote_Theater.Close"); boolean closed = false; if (Opened ()) { String connection_address = Conductor_Identity.Get (ADDRESS_PARAMETER_NAME); if (connection_address != null) { try {Send_Message (Message .Action (CONDUCTOR_DISCONNECT_ACTION) .Set (ADDRESS_PARAMETER_NAME, connection_address));} catch (IOException exception) {/* Nothing for it */} } super.Close (); closed = true; } Response (null); if ((DEBUG & DEBUG_CONNECTION) != 0) System.out.println ("<<< Remote_Theater.Close: " + closed); return closed; } /*============================================================================== Messages */ // Message_Delivered_Listener /** Get the identity description of this Conductor Management interface implementation.

@return A Message describing this Remote_Theater object. */ public Message Listener_Identity () {return REMOTE_THEATER_IDENTITY;} // Message_Delivered_Listener /** Take delivery of a Message.

The Action parameter value of the event's Message is used to determine an {@link #Action_Code(String) action code} that selects a handler method if the Action is known. The delivered Message is handled before returning. Most Messages will just be posted as a Response which will wake up any thread that is {@link #Wait_for_Response() waiting for a response}. Typically this happens as a result of a {@link #Send_and_Wait(Message) send-and_wait message}.

Messages that are asynchronously conveying {@link #Add_Processing_Listener(Processing_Listener) Conductor processing events} or {@link #Add_Log_Writer(Writer) Conductor log reports} are handled immediately.

Non-protocol messages are delivered to the {@link #Non_Protocol_Message_Listener(Message_Delivered_Listener) non-protocol listener}, if there is one. */ public void Message_Delivered ( Message_Delivered_Event event ) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Remote_Theater.Message_Delivered:" + NL +" Message -" + NL + event.Message.Routing () + NL + event.Message); switch (Action_Code (event.Message.Action ())) { // Asynchronous protocol messages: case PROCESSING_CHANGES_CODE: Processing_Event_Occurred (event.Message); break; case LOG_WRITER_CODE: Write_Log (event.Message); break; // Protocol response messages: case IDENTIFY_CODE: case CONDUCTOR_CONNECT_CODE: case CONDUCTOR_DISCONNECT_CODE: case IDENTITY_CODE: // START_CODE case PROCESSING_STATE_CODE: case STOP_CODE: // QUIT_CODE case CONFIGURATION_CODE: case SOURCES_CODE: case PROCEDURES_CODE: case GET_POLL_INTERVAL_CODE: // SET_POLL_INTERVAL_CODE case GET_RESOLVER_DEFAULT_VALUE_CODE: // SET_RESOLVER_DEFAULT_VALUE_CODE case GET_STOP_ON_FAILURE_CODE: // SET_STOP_ON_FAILURE_CODE case SEQUENTIAL_FAILURES_CODE: // RESET_SEQUENTIAL_FAILURES_CODE case PROCESSING_EXCEPTION_CODE: case CONDUCTOR_STATE_CODE: // ADD_PROCESSING_LISTENER_CODE // REMOVE_PROCESSING_LISTENER_CODE // ADD_LOG_WRITER_CODE // REMOVE_LOG_WRITER_CODE // ENABLE_LOG_WRITER_CODE Response (event.Message); break; // Basic Messages: case DONE_CODE: Close (); break; case UNDELIVERABLE_CODE: //!!! Error handling or reporting? Undeliverable_Messages++; Response (event.Message); Non_Protocol_Message (event); break; case NACK_CODE: //!!! Error handling or reporting? NACK_Messages++; Response (event.Message); // MESSENGERS_REPORT_CODE default: Non_Protocol_Message (event); break; } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Remote_Theater.Message_Delivered"); } private class Waiting_for_Response extends Thread { public void run () { try {sleep (Response_Timeout * 1000);} catch (InterruptedException exception) {} } } // End of Waiting_for_Response class. /** Send a Message and wait for a response.

The previous response is cleared, the Message is sent and then the current thread is blocked {@link #Wait_for_Response() waiting for a response}. When the response arrives it is returned.

@param message The Message to be sent. @return The response Message. This will be null if the connection to the Stage_Manager was lost or the response did not arrive before the {@link #Response_Timeout(int) response timeout} expired. Check if the Stage_Manager is {@link #Opened() opened} to determine the exact cause of a null response. @throws Remote_Management_Exception If Message could not be sent because it contains invalid PVL. */ protected Message Send_and_Wait ( Message message ) { Response = null; int tries = 0; while (true) { try {Send_Message (message); break;} catch (IOException exception) { if (exception instanceof EOFException) // Connection lost. return null; else if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Unable to send message -" + NL + message + NL + exception); } } return Wait_for_Response (); } /** Wait for a response Message.

If a non-null response is currently posted that will be immediately returned without any waiting. N.B.: The previously posted response must be cleared when it is no longer relevant or it will be incorrectly returned as the new response; {@link #Send_and_Wait(Message)}, for example, clears the response before sending a message that is expected to result in a new response.

After confirming that there is no response available a thread is started that will sleep until the {@link #Response_Timeout() response timeout} expires or it is woken when a response arrives. The current thread joins the waiting thread which prevents the method from returning until a response arrives or the timeout occurs.

@return The response Message. This will be null if a response timeout occurred before a response arrived. @throws IllegalThreadStateException If a waiting thread is active. */ protected Message Wait_for_Response () { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Remote_Theater.Wait_for_Response"); if (Waiting != null && Waiting.isAlive ()) throw new IllegalThreadStateException (ID + NL + "Already waiting for a response."); synchronized (Conductor_Identity) { if (Response != null) return Response; Waiting = new Waiting_for_Response (); Waiting.start (); } while (Waiting.isAlive ()) { try {Waiting.join ();} catch (InterruptedException exception) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("--- Remote_Theater.Wait_for_Response: interrupted"); } } Waiting = null; if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Remote_Theater.Wait_for_Response"); return Response; } /** Set the Stage_Manager response Message.

The Conductor_Identity is used as a lock object to synchronize the update of the Response Message and get the response Waiting thread. If the Waiting thread is not null and is alive then it is interrupted to release the Wait_for_Response block.

@param message The Message to be set as the Response. This may be null to clear the Response and stop the Waiting thread. */ private void Response ( Message message ) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Remote_Theater.Response:" + NL + message); Thread waiting; synchronized (Conductor_Identity) { Response = message; waiting = Waiting; } if (waiting != null && waiting.isAlive ()) { if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (" Interrupting the Waiting Thread"); try {waiting.interrupt ();} catch (SecurityException exception) {} catch (IllegalThreadStateException exception) {} } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Remote_Theater.Response"); } /*============================================================================== Management */ public Message Identity () { Protocol_Action_Response (IDENTITY_ACTION, null); return Response; } public void Start () {Protocol_Action (START_ACTION, null);} public void Stop () {Protocol_Action (STOP_ACTION, null);} public void Quit () {Protocol_Action (QUIT_ACTION, null);} public int Processing_State () { int tries = 0; while (true) { try {return Integer.parseInt (Protocol_Action_Response (PROCESSING_STATE_ACTION, VALUE_PARAMETER_NAME));} catch (NumberFormatException exception) { if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Invalid processing state value received."); } } } public Configuration Configuration () { Protocol_Action_Response (CONFIGURATION_ACTION, null); if (Response.Get (EXPLANATION_PARAMETER_NAME) != null) // No configuration. return null; Parameter group = Response.Find (CONFIGURATION_PARAMETER_NAME, Parameter.AGGREGATE); if (group == null) return null; Configuration configuration = null; Configuration.Include_Default = false; int tries = 0; while (true) { try { configuration = new Configuration (group); String name = Response.Get (NAME_PARAMETER_NAME); if (name != null) configuration.Name (name); break; } catch (Configuration_Exception exception) { if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Invalid Configuration received."); } } return configuration; } public Management Poll_Interval ( int seconds ) { Protocol_Action (SET_POLL_INTERVAL_ACTION, String.valueOf (seconds)); return this; } public int Poll_Interval () { int tries = 0; while (true) { try {return Integer.parseInt (Protocol_Action_Response (GET_POLL_INTERVAL_ACTION, VALUE_PARAMETER_NAME));} catch (NumberFormatException exception) { if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Invalid poll interval value received."); } } } public Management Resolver_Default_Value ( String value ) { Protocol_Action (SET_RESOLVER_DEFAULT_VALUE_ACTION, (value == null) ? "null" : value); return this; } public String Resolver_Default_Value () { String value = Protocol_Action_Response (GET_RESOLVER_DEFAULT_VALUE_ACTION, VALUE_PARAMETER_NAME); return (value.equals ("null") ? null : value); } public Management Stop_on_Failure ( int failure_count ) { Protocol_Action (SET_STOP_ON_FAILURE_ACTION, String.valueOf (failure_count)); return this; } public int Stop_on_Failure () { int tries = 0; while (true) { try {return Integer.parseInt (Protocol_Action_Response (GET_STOP_ON_FAILURE_ACTION, VALUE_PARAMETER_NAME));} catch (NumberFormatException exception) { if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Invalid stop on failure value received."); } } } public int Sequential_Failures () { int tries = 0; while (true) { try {return Integer.parseInt (Protocol_Action_Response (SEQUENTIAL_FAILURES_ACTION, VALUE_PARAMETER_NAME));} catch (NumberFormatException exception) { if (++tries == Message_Retries) throw new Remote_Management_Exception (ID + NL + "Invalid sequential failures value received."); } } } public Management Reset_Sequential_Failures () { Protocol_Action (RESET_SEQUENTIAL_FAILURES_ACTION, null); return this; } /** Get the most recent Exception from the Conductor.

: A generic Exception will be returned since the specific type was not conveyed through the messaging protocol (an enhancement could provide this...).

@return An Exception with the Conductor Exception message. This will be null if no Exception is obtained from the Conductor. */ public Exception Processing_Exception () { String value = Protocol_Action_Response (PROCESSING_STATE_ACTION, VALUE_PARAMETER_NAME); return (value.equals ("null") ? null : new Exception (value)); } public boolean Connected_to_Stage_Manager () {return Opened ();} /*------------------------------------------------------------------------------ Table Records */ public Vector> Sources () { Protocol_Action_Response (SOURCES_ACTION, null); return Table (Response.Value_of (TABLE_PARAMETER_NAME)); } public Vector> Procedures () { Protocol_Action_Response (PROCEDURES_ACTION, null); return Table (Response.Value_of (TABLE_PARAMETER_NAME)); } /*------------------------------------------------------------------------------ Protocol Message */ /** Send a protocol Message without waiting for a response.

A Message specifying the protocol action and, optionally, a protocol value, is generated and sent with routing to the Messenger having the Identity with which this object was constructed. The Message is expected to be delivered to a Local_Theater bound to a Conductor.

@param action The {@link Message#Action(String) Action} value of the Message. @param value The {@link #VALUE_PARAMETER_NAME} value to be added to the Message. If null, this parameter is not included. @throws Remote_Management_Exception If a PVL_Exception occured indicating an invalid Message. */ private void Protocol_Action ( String action, String value ) { try {Send_Message (Message .Action (action) .Set (VALUE_PARAMETER_NAME, value) .Route_To (Conductor_Identity));} catch (IOException exception) {/* Connection lost. */} } /** Send a protocol Message and wait for a response.

A Message specifying the protocol action is generated and sent with routing to the Messenger having the Identity with which this object was constructed. The Message is expected to be delivered to a Local_Theater bound to a Conductor. The current thread waits for a response. Then the validity of the response is checked. If a value was expected it is returned.

@param action The {@link Message#Action(String) Action} value of the Message. @param value_parameter_name The name of the response parameter that is expected to contain a response value. If null, no value is expected. @return The value of the response value_parameter_name parameter. This will be null if no value is expected. @throws Remote_Management_Exception If a protocol error occurred. This could be due to a PVL_Exception indicating an invalid Message, timeout while waiting for a response, a response that does not have a matching action parameter, or the response message not containing the required value parameter. */ private String Protocol_Action_Response ( String action, String value_parameter_name ) { String value = null; Message send = Message .Action (action) .Route_To (Conductor_Identity), response; int tries = 0; while (true) { response = Send_and_Wait (send); ++tries; if (response == null) { if (tries != Message_Retries) continue; throw new Remote_Management_Exception (ID + NL + "No " + action + " action response after " + Response_Timeout + " second" + ((Response_Timeout > 1) ? "s." : ".")); } if (! action.equals (response.Action ())) { if (tries != Message_Retries) continue; throw new Remote_Management_Exception (ID + NL + "Invalid " + action + " action response -" + NL + response); } if (value_parameter_name != null) { value = response.Get (value_parameter_name); if (value == null) { if (tries != Message_Retries) continue; throw new Remote_Management_Exception (ID + NL + "Invalid " + action + " action response." + NL + "Missing " + value_parameter_name + " parameter -" + NL + response); } } break; } return value; } private void Non_Protocol_Message ( Message_Delivered_Event event ) { Unknown_Messages++; if (Non_Protocol_Message_Listener != null) Non_Protocol_Message_Listener.Message_Delivered (event); } /*------------------------------------------------------------------------------ Processing_Changes */ public Processing_Changes State () { Protocol_Action_Response (CONDUCTOR_STATE_ACTION, null); return Processing_Changes (Response); } public Management Add_Processing_Listener ( Processing_Listener listener ) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (">>> Remote_Theater.Add_Processing_Listener:" + NL + listener); if (listener != null) { boolean report = false; synchronized (Processing_Listeners) { if (Processing_Listeners.isEmpty ()) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Listeners is empty"); Processing_Listeners.add (listener); Protocol_Action (ADD_PROCESSING_LISTENER_ACTION, null); } else if (! Processing_Listeners.contains (listener)) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Added to Processing_Listeners"); Processing_Listeners.add (listener); report = true; } if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + Processing_Listeners.size () + " Processing_Listeners"); } if (report) { // Report the current Conductor state. Processing_Changes changes = State (); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Reporting Processing_Changes -" + NL + changes); if (changes != null) listener.Processing_Event_Occurred (new Processing_Event (this, changes)); } } if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Remote_Theater.Add_Processing_Listener"); return this; } public boolean Remove_Processing_Listener ( Processing_Listener listener ) { boolean removed = false; if (listener != null) { synchronized (Processing_Listeners) { removed = Processing_Listeners.remove (listener); if (Processing_Listeners.isEmpty ()) Protocol_Action (REMOVE_PROCESSING_LISTENER_ACTION, null); } } return removed; } private void Processing_Event_Occurred ( Message message ) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (">>> Remote_Theater.Processing_Event_Occurred:" + NL + message); synchronized (Processing_Listeners) { if (Processing_Listeners.isEmpty ()) { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Listeners is empty" + NL +"<<< Remote_Theater.Processing_Event_Occurred"); return; } } Processing_Changes changes = Processing_Changes (message); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" Processing_Changes -" + NL + changes); if (changes == null) return; Processing_Event event = new Processing_Event (this, changes); synchronized (Processing_Listeners) { int index = -1, size = Processing_Listeners.size (); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (" " + size + " Processing_Listeners"); while (++index < size) Processing_Listeners.get (index) .Processing_Event_Occurred (event); } if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Remote_Theater.Processing_Event_Occurred"); } /*------------------------------------------------------------------------------ Log Writer */ public Management Add_Log_Writer ( Writer writer ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Remote_Theater.Add_Log_Writer_Route: " + writer); boolean add = Log_Writer.Is_Empty (); if (Log_Writer.Add (writer) && add) Protocol_Action (ADD_LOG_WRITER_ACTION, null); if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Remote_Theater.Add_Log_Writer_Route"); return this; } public boolean Remove_Log_Writer ( Writer writer ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Remote_Theater.Remove_Log_Writer_Route: " + writer); boolean removed = Log_Writer.Remove (writer); if (removed && Log_Writer.Is_Empty ()) Protocol_Action (REMOVE_LOG_WRITER_ACTION, null); if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Remote_Theater.Remove_Log_Writer_Route: " + removed); return removed; } public Management Enable_Log_Writer ( Writer writer, boolean enable ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Remote_Theater.Enable_Log_Writer_Route: " + writer + ' ' + enable); Protocol_Action (ENABLE_LOG_WRITER_ACTION, String.valueOf (enable)); Log_Writer.Suspend (writer, enable); if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Remote_Theater.Enable_Log_Writer_Route"); return this; } private void Write_Log ( Message message ) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (">>> Remote_Theater.Write_Log: " + NL + message); String string; if (message == null || (string = message.Get (LOG_WRITTEN_PARAMETER_NAME)) == null || string.length () == 0) { if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println (" No " + LOG_WRITTEN_PARAMETER_NAME + " parameter value" + NL +"<<< Remote_Theater.Write_Log"); return; } try {Log_Writer.Write (new String_Buffer (string).escape_to_special ().toString (), Messenger_Styled_Writer.Style_Parameter (message.Find (LOG_STYLE_PARAMETER_GROUP)));} catch (IOException exception) { //!!! Remove the offending writer. if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("!!! Log_Writer failed -" + NL + exception); } if ((DEBUG & DEBUG_LOG_WRITER) != 0) System.out.println ("<<< Remote_Theater.Write_Log"); } } pirl-2.3.8/PIRL/Conductor/Maestro/Stage_Manager.conf0000644000175000017500000000260411165522244022034 0ustar mathieumathieu/* Stage_Manager Configuration CVS ID: Stage_Manager.conf,v 1.8 2009/04/04 00:07:00 castalia Exp */ /* Communications port number. */ Port = 4144 /* Connection identify timeout (seconds). The minimum value is 5 seconds. */ Identify_Timeout = 15 /* Connection authentication password. If this parameter is not defined and the software has been set to disallow aunathenticated connections the Stage_Manager will log a notice and refuse to start. */ Password = "" /* Hello broadcast. The port number and multicast address for broadcasting the startup Hello message. If the port number is not positive or the address is empty the Hello broadcast will not be done. Default values will be used when a parameter is not provided. */ Hello_Port = 4170 Hello_Address = "230.1.2.3" /* Log file pathname. Use "-" to log to stdout. */ Log_Pathname = Stage_Manager.log /* Conductor command name. If this parameter is not defined, is empty, or set to "DISABLED", remote command execution is disabled. For best security the parameter should be defined as an absolute pathname. N.B.: The value of this parameter is only the command name; do not include any command line arguments. */ Conductor_Command_Name = "" /* Maximum command execution for a single request. */ Max_Start_Conductor_Count = 10 pirl-2.3.8/PIRL/Conductor/Maestro/Conductor_Table_Model.java0000644000175000017500000022260611742733133023532 0ustar mathieumathieu/* Conductor_Table_Model PIRL CVS ID: Conductor_Table_Model.java,v 1.42 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Conductor; import PIRL.Conductor.Management; import PIRL.Conductor.Manager; import PIRL.Conductor.Processing_Listener; import PIRL.Conductor.Processing_Event; import PIRL.Messenger.Message; import javax.swing.table.AbstractTableModel; import javax.swing.event.TableModelListener; import javax.swing.event.TableModelEvent; import javax.swing.SwingUtilities; import java.io.IOException; import java.util.List; import java.util.Vector; /** A Conductor_Table_Model contains a table of Conductor identities each with its associated Remote_Theater Management and Manager.

A Conductor is represented by its {@link Conductor#Identity() identity Message} which is supplemented by its Theater location plus any additional parameters that the application might find useful. The data model maintains a list of all known Conductor identities.

When a Conductor identity is added to the data model Remote_Theater Management is provided for it that uses a specified Theater. The data model will register itself with the Remote_Theater Management as a Processing_Listener. This is used to maintain the current Conductor processing state in each identity and to notify the listeners of this TableModel of state changes in addition to the usual data model content changes. Also, any TableModelListener may register with this data model to only receive notices of Conductor processing state changes.

A list of Managers associate with each Conductor is also maintained. A Condcutor is not required to have a Manager.

The TableModel interface that is implemented presents Theater locations in the first column and Conductor names in the second column.

@author Bradford Castalia, UA/PIRL @version 1.42 */ public class Conductor_Table_Model extends AbstractTableModel implements Processing_Listener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Conductor_Table_Model (1.42 2012/04/16 06:04:11)"; /** The names of the table columns. */ public static String[] Column_Names = {"Theaters", "Conductors"}; /** The index of the Theaters column in the data model. */ public static final int THEATERS_COLUMN = 0; /** The index of the Conductos column in the data model. */ public static final int CONDUCTORS_COLUMN = 1; /** Conductor identity parameters of interest to the data model. */ public static final String // Theater location. THEATER_LOCATION_PARAMETER_NAME = Kapellmeister.THEATER_LOCATION_PARAMETER_NAME, ADDRESS_PARAMETER_NAME = Kapellmeister.ADDRESS_PARAMETER_NAME, CONDUCTOR_ID_PARAMETER_NAME = Conductor.CONDUCTOR_ID_PARAMETER, // Pipeline name. PIPELINE_PARAMETER_NAME = Conductor_Definition.PIPELINE_PARAMETER_NAME, // Processing state. PROCESSING_STATE_PARAMETER_NAME = Theater.PROCESSING_STATE_ACTION; private Vector Identities = new Vector (); private Vector Managements = new Vector (); private Vector Managers = new Vector (); private Vector State_Change_Listeners = new Vector (); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_SET_IDENTITY = 1 << 2, DEBUG_PROFILE = 1 << 3, DEBUG_REPLACE = 1 << 4, DEBUG_OPEN_THEATER = 1 << 5, DEBUG_MANAGEMENT = 1 << 6, DEBUG_LISTENER = 1 << 7, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct an empty Conductor_Table_Model. */ public Conductor_Table_Model () {} /*============================================================================== AbstractTableModel */ /** Find the data model column index of a name.

@param name A column name String. May be null. @return The index of the named column in the data model. This will be -1 if the name does not match (case insensitive) a column name. */ public int findColumn ( String name ) { if (name != null) { for (int column = 0; column < Column_Names.length; column++) if (name.equalsIgnoreCase (Column_Names[column])) return column; } return -1; } /*============================================================================== TableModel */ /** Get the Class for a data model column.

@param column A data model column index. @return If the column index is valid String.class is returned; otherwise Object.class is returned. */ public Class getColumnClass ( int column ) { if (column >= 0 && column < Column_Names.length) return String.class; return Object.class; } /** Get the number of columns in the data model.

@return The size of the {@link #Column_Names} array. */ public int getColumnCount () {return Column_Names.length;} /** Get the name of a data model column.

@param column A data model column index. @return The column name String. This will be null.if the column index is not valid. */ public String getColumnName ( int column ) { if (column >= 0 && column < Column_Names.length) return Column_Names[column]; return null; } /** Get the number of rows in the data model.

@return The number of Conductor identities contained in the model. */ public int getRowCount () {return Identities.size ();} /** Get the value of a data model cell.

For the {@link #THEATERS_COLUMN} the value will be the {@link #Location(int) Theater location}. For the {@link #CONDUCTORS_COLUMN} the value will be the {@link #Conductor_Name(int) Conductor name}.

@param row A data model row index. @param column A data model column index. @return The String value of the data model cell. For the {@link #THEATERS_COLUMN} the value will be the {@link Theater#Location(String) abbreviated location} of the {@link #Location(int) Theater location} for the row. For the {@link #CONDUCTORS_COLUMN} the value will be the {@link #Conductor_Name(int) Conductor name} of the row. This will be null if either of the data model index values are invalid. */ public Object getValueAt ( int row, int column ) { if (column >= 0 && column < getColumnCount ()) { switch (column) { case THEATERS_COLUMN: return Theater.Location (Location (row)); case CONDUCTORS_COLUMN: return Conductor_Name (row); } } return null; } /** Test if a data model cell is editable.

@param row A data model row index. @param column A data model column index. @return Always returns false. */ public boolean isCellEditable ( int row, int column ) {return false;} /*============================================================================== Accessors */ /** Get a Profile of the Conductors.

@return A Profile representing all the Conductors on all the Theaters listed in this data model. @throws If a Conductor identity can not be used to construct a valid Conductor_Definition or the definition is in conflict with another of the same name. */ public Profile Profile () throws IllegalArgumentException { if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (">>> Conductor_Table_Model.Profile"); Profile profile = new Profile (); int index = -1, size = Identities.size (); while (++index < size) { Message identity = Identities.get (index); if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (" Adding Conductor identity for Theater " + Theater_Location (identity) + NL + identity); profile.Add (Theater_Location (identity), identity); } if ((DEBUG & DEBUG_PROFILE) != 0) System.out.println (profile + NL +"<<< Conductor_Table_Model.Profile"); return profile; } /*------------------------------------------------------------------------------ Identities */ /** Add a Conductor identity to the data model.

The data model listeners are notified that a new row has been inserted.

N.B.: If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained the identity is not added to the the data model and no listener notification is sent; an IOException is thrown in this case.

@param identity A Conductor identity Message. If null, or the identity is already {@link #Index(Message) present} in the data model, nothing is done. @param theater The Theater where the Stage_Manager is located from which the Conductor identities were obtained. If null, nothing is done. @param client_identity The Message containing client authentication information for connecting to the Stage_Manager. If null, nothing is done. @return This Conductor_Table_Model. @throws IOException If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained. @see #Set_Identity(Message, Theater, Message) */ public Conductor_Table_Model Identity ( Message identity, Theater theater, Message client_identity ) throws IOException { if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println (">>> Conductor_Table_Model.Identity"); if (theater != null && identity != null && Index (identity) < 0) { if (Set_Identity (identity, theater, client_identity)) Notify_Inserted (getRowCount () - 1); } if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println ("<<< Conductor_Table_Model.Identity"); return this; } /** Set a Conductor identity.

N.B.: This method should only be used to set a new Conductor identity by a method that does the neccessary state checks and table view update notification.

The {@link #THEATER_LOCATION_PARAMETER_NAME} is set at the beginning of the identity with the {@link Theater#Location() Remote_Theater location} before the identity is added to the data model along with null Managements and Managers entries. The Theater location in the identity is necessary to correctly {@link #Matching_Index(Message) match} identities. This is particularly critical when identities are {@link #Replace(List, Theater, Message) replaced} which depends on recognizing previously added identities by matching Theater locations.

The {@link Message#Name(String) name} of the identity is set to the identity's {@link #Conductor_Name(Message) Conductor name}.

The identity is added to the data model along with null place holders for the Remote_Theater Management and Conductor Manager. Then the new {@link #Management(Message, Theater, Message) Management} is obtained and {@link #Set_Management(int, Remote_Theater) set} in the data model.

N.B.: If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained the tentative new data model row is removed and an IOException is thrown.

N.B.: No data model listener notification is done.

@param identity A Conductor identity Message.N.B.: Must not be null; there should be no null identities in the data model. The identity must also be unique (not already present) in the data model. @param theater The Theater where the Stage_Manager is located from which the Conductor identities were obtained. Must not be null. @param client_identity The Message containing client authentication information for connecting to the Stage_Manager. Should not be null. @return true if the identity was set; false if the Theater location is invalid and not entered in the data model. @throws IOException If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained. @see #Management(Message, Theater, Message) */ protected boolean Set_Identity ( Message identity, Theater theater, Message client_identity ) throws IOException { if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println (">>> Conductor_Table_Model.Set_Identity:" + NL +" identity -" + NL + identity + NL +" theater -" + NL + theater + NL +" client_identity -" + NL + client_identity); /* >>> WARNING <<< The Theater location in the identity is necessary to associate identities with subsequent searches by Theater. This is particularly critical for the Replace method which will not be able to recognize previously added identities without a matching Theater location in their identities. */ String location = theater.Location (); if (Theater.Port (location) <= 0) { // Invalid Theater location. if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println (" Invalid Theater location: " + location + NL +"<<< Conductor_Table_Model.Set_Identity: true"); return false; } identity .Set (THEATER_LOCATION_PARAMETER_NAME, location, 0); Identities.add (identity); /* The Conductor identity name is used a reference. N.B.: It should uniquely identify all match Conductor_Definitions. */ identity.Name (Conductor_Name (identity)); // Place holders. Managements.add (null); Managers.add (null); if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println (" Setting the Management for the Identity"); IOException exception = null; try {Set_Management (Identities.size () - 1, Management (identity, theater, client_identity));} catch (IOException except) {exception = except;} catch (IllegalArgumentException except) { exception = new IOException (except.getMessage ()); exception.initCause (except); } if (exception != null) { // Backout the identity that failed to obtain a Management. if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println (" Set_Identity failed to obtain Management -" + NL + exception); int row = Identities.size () - 1; Identities.remove (row); Managements.remove (row); Managers.remove (row); throw exception; } if ((DEBUG & DEBUG_SET_IDENTITY) != 0) System.out.println ("<<< Conductor_Table_Model.Set_Identity: true"); return true; } /** Get the Identity of a Conductor at a row index.

@param row A data model row index. @return A Conductor identity Message. This will be null if the row index is less than zero or greater than or equal to the {@link #getRowCount() total data model rows}. */ public Message Identity ( int row ) { if (row >= 0 && row < getRowCount ()) return Identities.get (row); return null; } /** Get the Identity of a Conductor associated with a Remote_Theater Management.

@param management A Remote_Theater Management reference. @return A Conductor identity Message. This will be null if the management could not be {@link #Index(Remote_Theater) found} in the data model. */ public Message Identity ( Remote_Theater management ) {return Identity (Index (management));} /** Get the Identity of a Conductor associated with a Manager.

@param manager A Manager reference. @return A Conductor identity Message. This will be null if the manager could not be {@link #Index(Manager) found} in the data model. */ public Message Identity ( Manager manager ) {return Identity (Index (manager));} /** Get the data model row index of a Conductor identity.

@param identity A Conductor identity Message. May be null. @return The data model index of the Conductor identity, or -1 if the identity is not present. */ public int Index ( Message identity ) { int row = -1, rows = getRowCount (); while (++row < rows) if (identity == Identities.get (row)) return row; return -1; } /** Get the data model row index of a Conductor with a matching identity.

N.B.: This method searches the data model for a Conductor identity that {@link #Matching_Index(Message, List) matches} the specified identity. The matching identity need not be the {@link #Index(Message) same} object.

@param identity A Conductor identity Message. @return The data model row index of the matching identity. This will be -1 if a matching identity could not be found in the data model. @see #Matching_Index(Message, List) */ public int Matching_Index ( Message identity ) {return Matching_Index (identity, Identities);} /** Get the index of an identity Message in a list of identities.

The index of the first identities entry that matches the specified identity is returned. An identity matches if either it has the same {@link #ADDRESS_PARAMETER_NAME}, or all the values of the {@link #THEATER_LOCATION_PARAMETER_NAME}, {@link #CONDUCTOR_ID_PARAMETER_NAME} and {@link #PIPELINE_PARAMETER_NAME} parameters are the same.

@param identity A Conductor {@link Conductor#Identity() identity} Message supplemented with a {@link #THEATER_LOCATION_PARAMETER_NAME} parameter. If null, -1 is returned. @param identities A List of Conductor identities. If null, -1 is returned. @return The index of the entry in the identities list that matches the identity, or -1 if no match is found. */ protected static int Matching_Index ( Message identity, List identities ) { if (identity == null || identities == null || identities.isEmpty ()) return -1; String address = identity.Get (ADDRESS_PARAMETER_NAME); if (address != null) for (int index = 0; index < identities.size (); index++) if (address.equals (((Message)identities.get (index)) .Get (ADDRESS_PARAMETER_NAME))) return index; String theater_location = identity.Get (THEATER_LOCATION_PARAMETER_NAME), conductor_ID = identity.Get (CONDUCTOR_ID_PARAMETER_NAME), pipeline = identity.Get (PIPELINE_PARAMETER_NAME); if (theater_location == null || conductor_ID == null || pipeline == null) return -1; int index = identities.size (); while (--index >= 0) { identity = identities.get (index); if (theater_location .equals (identity.Get (THEATER_LOCATION_PARAMETER_NAME)) && conductor_ID .equals (identity.Get (CONDUCTOR_ID_PARAMETER_NAME)) && pipeline .equals (identity.Get (PIPELINE_PARAMETER_NAME))) return index; } return -1; } /** Replace all identities for a specified Remote_Theater Management with new identities.

Each current identity in the data model that has a {@link #THEATER_LOCATION_PARAMETER_NAME} value that matches the {@link Theater#Location() Theater location} is compared against the new identities list. An attempt is made to get the {@link #Matching_Index(Message, List) index} of the existing identity in the new identities list. If a matching identity can not be found the data model row for the current identity is removed; the current identity is defunct. If a matching identity is found the identity is removed from the list of new identities; the identity is redundant. This checking removes all current identities that are not in the new identities list, and removes from the new identities list all identities that are already in the current identitites list. After all current identities have been checked all the remaining new {@link #Set_Identity(Message, Theater, Message) identities are added} to the data model along with the Remote_Theater Management and an equal number of null Manager entries.

A {@link #Notify_Table_Changed() table data changed notification} is sent to the data model listeners only if any changes occured to the data model.

N.B.: If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained for an identity it is not added to the the data model. In this case, after an attempt has been made to add all the new identities to the model an IOException will thrown. Because multiple exceptions might have occured a generic IOException is thrown containing a message that lists each specific exception message, including its specific type, with each exception message separated from the one that prceeded it by a single line containing seven dash ('-') characters.

@param identities A List of Conductor {@link Conductor#Identity() identity} Messages. If null, nothing is done. @param theater The Theater where the Stage_Manager is located from which the Conductor identities were obtained. If null, nothing is done. @param client_identity The Message containing client authentication information for connecting to the Stage_Manager. If null, nothing is done. @throws IOException If {@link #Management(Message, Theater, Message) Remote_Theater Management} could not be obtained. @see #Matching_Index(Message, List) @see #Set_Identity(Message, Theater, Message) */ public Conductor_Table_Model Replace ( List identities, Theater theater, Message client_identity ) throws IOException { if (identities == null || theater == null) return this; if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (">>> Conductor_Table_Model.Replace:" + NL +" " + identities.size () + " Identities -" + NL + identities + NL +" theater -" + NL + theater + NL +" client_identity -" + NL + client_identity); String theater_location = theater.Location (); boolean changed = false; int index, row = getRowCount (); Message identity; while (--row >= 0) { identity = Identities.get (row); if (! theater_location .equals (identity.Get (THEATER_LOCATION_PARAMETER_NAME))) continue; if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (NL +" Identity at row " + row + " -" + NL + identity); // Find the matching current identity in the new identities list. index = Matching_Index (identity, identities); if (index < 0) { // Defunct: Current identity is not in the new list. if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (" Obsolete"); Remove_at (row); changed = true; } else { // Redundant: Current identity is in the new list. if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (" Redundant"); identities.remove (index); while ((index = Matching_Index (identity, identities)) >= 0) identities.remove (index); } } // What's left are new identities. String report = null; if (identities.size () > 0) { if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (NL + " Adding " + identities.size () + " Identities"); index = -1; row = identities.size (); while (++index < row) { if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (NL +" Conductor_Table_Model.Replace: Identity -" + NL + identities.get (index)); try { if (Set_Identity (identities.get (index), theater, client_identity)) changed = true; } catch (IOException exception) { // Accumulate exception reports if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (" IOException -" + NL + exception); if (report == null) report = exception.toString (); else report += NL + "--------" + NL + exception; report += NL + "For identity -" + NL + identities.get (index); } } } if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println (" Conductor_Table_Model.Replace: changed = " + changed); if (changed) Notify_Table_Changed (); if (report != null) throw new IOException (report); if ((DEBUG & DEBUG_REPLACE) != 0) System.out.println ("<<< Conductor_Table_Model.Replace"); return this; } /** Get the name of a Conductor at a data model index.

@param row A data model row index. @return The {@link #Conductor_Name(Message) Conductor name} from the {@link #Identity(int) identity at the row index}. This will be null if the row index is invalid or the identity does not contain the required information. */ public String Conductor_Name ( int row ) {return Conductor_Name (Identity (row));} /** Get the Theater location of a Conductor at a data model index.

The fully qualified Theater location is returned.

@param row A data model row index. @return The {@link #Theater_Location(Message) Theater location} from the {@link #Identity(int) identity at the row index}. This will be null if the row index is invalid or the identity does not contain the required information. */ public String Location ( int row ) {return Theater_Location (Identity (row));} /** Get the processing state of a Conductor at a data model index.

If the {@link #Identity(int) row's Conductor identity} has a {@link #Processing_State(Message) procesing state} of zero an attempt is made to use the {@link #Management(int) row's Management} to {@link Management#Processing_State() obtain the current procesing state}. If this is successful the identity is updated with the current processing state.

@param row A Conductor row index. @return The Conductor {@link #Processing_State(Message) processing state}. This will be zero if the row index is outside the range of available Conductors or the processing state has not yet been obtained from the Conductor. */ public int Processing_State ( int row ) { int processing_state = 0; Message identity = Identity (row); if (identity != null && (processing_state = Processing_State (identity)) == 0) { Remote_Theater management = Management (row); if (management != null && management.Opened ()) { try { processing_state = management.Processing_State (); Processing_State (identity, processing_state); } catch (Remote_Management_Exception exception) {} } } return processing_state; } /*------------------------------------------------------------------------------ Managements */ /** Get the Remote_Theater Management at a data model row index.

N.B.: The current Remote_Theater Management data model row is obtained as-is; Remote_Theater Management will not be {@link #Open_Management(Message, Theater, Message) opened or re-opened}.

@param row A data model row index. @return The Remote_Theater Management for the data model row. This will be null if the row index is not valid or no Remote_Theater Management is present at the index. */ public Remote_Theater Management ( int row ) { if (row >= 0 && row < getRowCount ()) return Managements.get (row); return null; } /** Get the Remote_Theater Management for a Conductor.

N.B.: The current Remote_Theater Management associated with the Conductor identity is obtained as-is; Remote_Theater Management will not be {@link #Open_Management(Message, Theater, Message) opened or re-opened}.

@param identity A Conductor identity Message. @return The Remote_Theater Management associated with the Conductor identity. This will be null if the Conductor identity is null or not found in the data model. @see #Index(Message) @see #Management(int) */ public Remote_Theater Management ( Message identity ) {return Management (Index (identity));} /** Get Remote_Theater Management for a Conductor.

If the Conductor already has Remote_Theater Management and it is {@link Remote_Theater#Opened() open}, that is returned. Otherwise an attempt is made to obtain it.

Remote_Theater Management can be obtained when the Theater and a client identity containing any authentication information required by the Stage_Manager are provided. If Remote_Theater Management was found for the Conductor but it is closed, it is re-opened at the same location. Otherwise a {@link #Management(Message, Theater, Message) new Remote_Theater Management} is constructed and {@link #Management(int, Remote_Theater) registered}.

@param conductor_identity The identity Message of the Conductor to be opened. If Remote_Theater Management is to be constructed or re-opened this must not be null and must contain an {@link #ADDRESS_PARAMETER_NAME} parameter. @param theater The Theater where the Stage_Manager is located from which the Conductor identity was obtained. If Remote_Theater Management is to be constructed or re-opened this must not be null. @param client_identity The Message containing the {@link Theater#KEY_PARAMETER_NAME} that may be needed by the Stage_Manager to construct or re-open Remote_Theater Management. N.B.: This may be null if the Stage_Manager allows unauthenticated connections; otherwise the Stage_Manager will reject the connection. @return An open Remote_Theater Management for the Conductor. This will be null if the identity could not be found in the data model or the theater or client_identity is null and a Remote_Theater Management was needed to be constructed or re-opened @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there were any problems in the content of the Messages used to establish the connection. */ public Remote_Theater Open_Management ( Message conductor_identity, Theater theater, Message client_identity ) throws IOException { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Conductor_Table_Model.Open_Management:" + NL +" Identity -" + NL + conductor_identity + NL +" Theater -" + NL + theater); Remote_Theater management = null; int row = Index (conductor_identity); if (row >= 0) { management = Managements.get (row); if (management == null || ! management.Opened ()) { if (theater == null || client_identity == null) management = null; else { if (management == null) { // Open with new Managment. if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Opening new management"); management = Management (conductor_identity, theater, client_identity); // Apply the new Management. Management (row, management); } else { // Re-open with existing Managment. if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Re-opening management:" + NL + management); management.Open ( theater.Messenger ().Client_Hostname (), theater.Messenger ().Client_Port (), client_identity, conductor_identity.Get (ADDRESS_PARAMETER_NAME) ); } } } } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Remote_Theater Management -" + NL + management + NL +"<<< Conductor_Table_Model.Open_Management"); return management; } /** Get Remote_Theater Management for a Conductor.

Remote_Theater Management services are obtained for a Theater associated with a Stage_Manager. The client identity {@link Message#NAME_PARAMETER_NAME} is qualified with the {@link Remote_Theater#REMOTE_THEATER_NAME} following a '/' delimiter if it does not already contain this qualifier name. A Remote_Theater connection is then established with the Stage_Mangager at the specified Theater using the authentication information in the client identity, and the Remote_Theater Messenger is linked to the Conductor Messenger at the {@link #ADDRESS_PARAMETER_NAME} found in the Conductor's identity information. This is done by constructing a new Remote_Theater that will provide Management communication directly with the Conductor.

@param conductor_identity The identity Message of the Conductor to be opened. Must not be null and must contain an {@link #ADDRESS_PARAMETER_NAME} parameter. @param theater The Theater where the Stage_Manager is located from which the Conductor identity was obtained. Must not be null. @param client_identity The Message containing the {@link Theater#KEY_PARAMETER_NAME}. @throws IOException If a connection could not be established to the Stage_Manager. This will be a Theater_Protocol_Exception if there were any problems in the content of the Messages used to establish the connection. @throws IllegalArgumentException If the argument requirements are not satsified, or the Conductor identity does not have an {@link #ADDRESS_PARAMETER_NAME} parameter. @see Remote_Theater#Open(String, int, Message, String) */ public static Remote_Theater Management ( Message conductor_identity, Theater theater, Message client_identity ) throws IOException { if ((DEBUG & DEBUG_OPEN_THEATER) != 0) System.out.println (">>> Management:" + NL +" conductor_identity -" + NL + conductor_identity + NL +" theater -" + NL + theater + NL +" client_identity -" + NL + client_identity); if (conductor_identity == null) throw new IllegalArgumentException (ID + NL + "Can't get remote Theater management without a Conductor identity."); if (theater == null) throw new IllegalArgumentException (ID + NL + "Can't get remote Theater Management without a Theater."); // Qualify the client name. String client_name = client_identity.Get (Message.NAME_PARAMETER_NAME); if (client_name == null || client_name.indexOf (Remote_Theater.REMOTE_THEATER_NAME) < 0) { if (client_name == null) client_name = Remote_Theater.REMOTE_THEATER_NAME; else client_name += "/" + Remote_Theater.REMOTE_THEATER_NAME; client_identity.Set (Message.NAME_PARAMETER_NAME, client_name); } if ((DEBUG & DEBUG_OPEN_THEATER) != 0) System.out.println (" Constructing Remote_Theater -" + NL +" Hostname - " + theater.Messenger ().Client_Hostname () + NL +" Port - " + theater.Messenger ().Client_Port () + NL +" Client Identity -" + NL + client_identity + NL +" Conductor Address - " + conductor_identity.Get (ADDRESS_PARAMETER_NAME)); /* N.B.: Can throw IllegalArgumentException if */ Remote_Theater management = new Remote_Theater ( theater.Messenger ().Client_Hostname (), theater.Messenger ().Client_Port (), client_identity, conductor_identity.Get (ADDRESS_PARAMETER_NAME) ); if ((DEBUG & DEBUG_OPEN_THEATER) != 0) System.out.println (management + NL +"<<< Management"); return management; } /** Get the data model row index for a Remote_Theater Management.

@param management A Remote_Theater Management object. @return The data model row index containing the Remote_Theater Management, or -1 if the Management was not found in the data model. */ public int Index ( Remote_Theater management ) {return Managements.indexOf (management);} /** Get the data model row index of the next Conductor entry at a Theater location starting after a given index.

Each Conductor identity, beginning with the data model entry immediately following the specified index, has the value of its {@link #THEATER_LOCATION_PARAMETER_NAME} parameter compared with the specified Theater location. The index of the first one that matches is returned.

@param theater_location A String specifying a Theater location. The Theater location is {@link Theater#Full_Location(String) fully qualified} to ensure it will correctly match the standard locations stored with the Conductor identities. @param index The data model row index after which the search for the next matching Conductor identity will begin. If less than zero the search begins with the first row. @return The index of the next Conductor identity that contains a matching Theater location, or -1 if no match is found or the Theater location is null or the empty String. @see Theater#Location() */ public int Next_Index ( String theater_location, int index ) { // Fully qualify the Theater location. theater_location = Theater.Full_Location (theater_location); if (theater_location != null) { if (index < -1) index = -1; int size = getRowCount (); while (++index < size) if (theater_location.equals (Identities.get (index) .Get (THEATER_LOCATION_PARAMETER_NAME))) return index; } return -1; } /** Get the count of Conductors identities on a Theater that match a Conductor_Definition.

@param theater_location The String specifying a Theater location (does not need to be fully qualified). If null, the count will be zero. @param conductor_definition A Conductor_Definition to {@link Conductor_Definition#Matches(Message) match} against Conductor identities. @return The count of Conductor identities associated with the Theater location that match the Conductor_Definition. @see #Next_Index(String, int) */ public int Count ( String theater_location, Conductor_Definition conductor_definition ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Conductor_Table_Model.Count:" + NL +" theater_location - " + theater_location + NL +" conductor_definition -" + NL + conductor_definition); int count = 0; if (conductor_definition != null) { int index = -1; while ((index = Next_Index (theater_location, index)) >= 0) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Identity " + index + " -" + NL + Identity (index)); if (conductor_definition.Matches (Identity (index))) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Matches"); ++count; } else if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" No match"); } } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Conductor_Table_Model.Count: " + count); return count; } /** Set the Remote_Theater Management for a data model row index.

If the Remote_Theater Management is identical to the current entry, or the row does not have a Conductor identity, nothing is done.

If the current Remote_Theater Management entry for the row is non-null the row is closed before the entry is replaced. The {@link #Processing_State(Message) current Conductor processing state} is replaced with the {@link Management#Processing_State() state obtained from the new Management}, or zero if the new Management is null. This Conductor_Table_Model is registered with the new Managment, if non-null, {@link Management#Add_Processing_Listener(Processing_Listener) to receive} {@link #Processing_Event_Occurred(Processing_Event) Conductor processing state change notifications}. Finally, if the the processing state changed all table listeners are {@link #Notify_State_Changed(int, int) notified of the new state}.

@param row A data model row index. If not a valid data model row index nothing is done. @param management A Remote_Theater Management object. If this is identical to the {@link #Management(int) existing Management} nothing is done. @return This Conductor_Table_Model. */ public Conductor_Table_Model Management ( int row, Remote_Theater management ) { if (row >= 0 && row < getRowCount ()) { int processing_state = Set_Management (row, management); if (processing_state != UNCHANGED_STATE) Notify_State_Changed (row, processing_state); } return this; } /** Set the Remote_Theater Management for a Conductor. <> @param identity A Conductor identity Message. @param management A Remote_Theater Management object. If this is identical to the {@link #Management(int) existing Management} nothing is done. @return This Conductor_Table_Model. @see #Management(int, Remote_Theater) */ public Conductor_Table_Model Management ( Message identity, Remote_Theater management ) {return Management (Index (identity), management);} /** Special {@link #Processing_State(Message) Conductor processing state} value that indicates no change from the current state.

When Remote_Theater Management is {@link #Close_Management(int) closed} or {@link #Set_Management(int, Remote_Theater) set} the previous processing state is returned for use in notifying processing state change listeners. If the state was unchanged this special unchanged value is returned instead. This enables the caller to avoid the cost of the notification and consequent table view updates. */ protected static final int UNCHANGED_STATE = -99999; /** Set a Remote_Theater Management.

N.B.: This method should only be used to set a new Remote_Theater Management by a method that does the neccessary state checks and table view update notification.

If the row does not have a Conductor identity or the Remote_Theater Management is identical to the current entry, nothing is done.

Management for the row is {@link #Close_Management(int) closed}. This will obtain the previous {@link #Processing_State(Message) Conductor processing state} or the {@link #UNCHANGED_STATE} value.

If the new Management is non-null it is set in the data model. The {@link #Processing_State(Message) current Conductor processing state} is obtained from the new Management and compared with the previous state (or zero if the previous state before closure was unchanged). If the state changed the Conductor identity is updated with the {@link #Processing_State(Message, int) new processing state}.

N.B.: No data model listener notification is done.

@param row A data model row index. @param management A Remote_Theater Management object. May be null. @return The previous processing state from the row's identity. This will be {@link #UNCHANGED_STATE} if the row has no identity (shouldn't happen), the existing Management is identical to the new Management, or the processing state is unchanged. @see #Close_Management(int) */ protected int Set_Management ( int row, Remote_Theater management ) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Set_Management: " + row + NL + management); int old_processing_state = UNCHANGED_STATE; Message identity = Identity (row); if (identity != null && management != Managements.get (row)) { // Close any previous management. if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Closing any previous Management"); int new_processing_state = old_processing_state = Close_Management (row); if (management != null) { // Set the new management. Managements.set (row, management); // Update the processing state. if (old_processing_state == UNCHANGED_STATE) old_processing_state = 0; if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Old processing state: " + old_processing_state); try {new_processing_state = management.Processing_State ();} catch (Remote_Management_Exception exception) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Failed to get Management.Processing_State -" + NL + exception); } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" New processing state: " + new_processing_state); if (new_processing_state != old_processing_state) Processing_State (identity, new_processing_state); else old_processing_state = UNCHANGED_STATE; // Register for processing state updates. management.Add_Processing_Listener (this); } } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println ("<<< Set_Management: " + old_processing_state); return old_processing_state; } /** Test if the Remote_Theater Management at a data model index is open.

@param row A data model row index. @return true if the Conductor {@link #Management(int) Remote_Theater Management at the row index} has been {@link Remote_Theater#Opened() opened}; false if the row index is invalid, no Remote_Theater Management is present at the row index, or the Remote_Theater is not open. */ public boolean Opened ( int row ) { boolean opened = false; if (row >= 0 && row < getRowCount ()) { Remote_Theater management = Managements.get (row); if (management != null) opened = management.Opened (); } return opened; } /** Test if the Remote_Theater associated with a Conductor identity is open.

@param identity A Conductor identity Message. @return true if the Conductor identity is associated with an open Remote_Theater Management; false if the identity is not present, has no associated Remote_Theater Management, or the Remote_Theater is not open. @see #Opened(int) */ public boolean Opened ( Message identity ) {return Opened (Index (identity));} /** Close the Remote_Theater Management at a data model index.

N.B.: This method should only be used to close a Remote_Theater Management by a method that does the neccessary state checks and table view update notification.

If a {@link #Manager(int) Manager} is present for the row it is {@link Manager#Disable() disabled}.

This Conductor_Table_Model is {@link Remote_Theater#Remove_Processing_Listener(Processing_Listener) unregisterd} as a processing listener of the Remote_Theater Management which is removed from (nullified in) the data model. N.B.: No data model listener notification is done.

@param row A data model row index. @return The previous processing state from the row's identity. This will be {@link #UNCHANGED_STATE} if the row has no identity (shouldn't happen) or the processing state is unchanged. */ protected int Close_Management ( int row ) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Close_Management: " + row); int previous_state = UNCHANGED_STATE; Message identity = Identity (row); if (identity != null) { Manager manager = Manager (row); if (manager != null) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Disabling the Manager"); manager.Disable (); } Remote_Theater management = Managements.get (row); if (management != null) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Removing Remote_Theater Management -" + management); // Remove from the data model. Managements.set (row, null); // Unregister for processing state updates. if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Remove_Processing_Listener"); management.Remove_Processing_Listener (this); // Close the management. if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Close the Management"); management.Close (); // Update the processing state. previous_state = Processing_State (identity); Processing_State (identity, 0); if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Old processing state: " + previous_state); if (previous_state == 0) previous_state = UNCHANGED_STATE; } } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println ("<<< Close_Management: " + previous_state); return previous_state; } /*------------------------------------------------------------------------------ Managers */ /** Get the Conductor Manager for a data model row index.

@param row A data model row index. @return A Conductor Manager. This will be null if the row index is not valid or no Manager is present at the index. */ public Manager Manager ( int row ) { if (row >= 0 && row < getRowCount ()) return Managers.get (row); return null; } /** Get the Manager for a Conductor.

@param identity A Conductor identity Message. @return A Conductor Manager. This will be null if the Conductor identity was null or not found. */ public Manager Manager ( Message identity ) {return Manager (Index (identity));} /** Set the Manager for a data model row index.

If the row has a non-null Manager entry that Manager is {@link Manager#Close() closed} before the entry is replaced. If the new Manager is not null the {@link #Management(int, Remote_Theater) Management is set} to new {@link Manager#Management() Manager's Management}.

@param row A data model row index. If not a valid data model row index nothing is done. @param manager A Conductor Manager. @return This Conductor_Table_Model. */ public Conductor_Table_Model Manager ( int row, Manager manager ) { if (row >= 0 && row < getRowCount ()) { Manager current_manager = Managers.get (row); if (current_manager != null && current_manager != manager) current_manager.Close (); Managers.set (row, manager); if (manager != null) Management (row, (Remote_Theater)manager.Management ()); } return this; } /** Get the data model row index for a Conductor Manager.

@param manager A Conductor Manager. @return The data model row index containing the Manager, or -1 if the Manager was not found. */ public int Index ( Manager manager ) {return Managers.indexOf (manager);} /** Open a Conductor Manager at a data model row index.

If a {@link #Manager(int) Manager is present} at the data model row it is returned. Otherwise, if {@link #Management(int) Remote_Theater Management is present} at the data model row and it is {@link Remote_Theater#Opened() opened}, it is used to construct a new Manager which is set for the data model row and then returned.

@param row A data model row index. @return A Conductor Manager. This will be null if the row index is invalid, or an opened Remote_Theater Management is not available for the row. @throws Remote_Management_Exception If a problem was encountered while constructing a new Manager. */ public Manager Open_Manager ( int row ) throws Remote_Management_Exception { Manager manager = null; if (row >= 0 && row < getRowCount ()) { manager = Managers.get (row); if (manager == null) { Remote_Theater management = Managements.get (row); if (management != null && management.Opened ()) { manager = new Manager (management); Managers.set (row, manager); } } } return manager; } /** Close a Conductor Manager at a data model row index.

If a {@link #Manager(int) Manager} is present at the data model row index it is {@link Manager#Close() closed} and it's entry removed from the data model.

@param row A data model row index. @return true if the Manager was present in the data model and has been closed; false if the Manager was not present. */ public boolean Close_Manager ( int row ) { Manager manager = Manager (row); if (manager != null) { manager.Close (); Managers.set (row, null); return true; } return false; } /** Close a Conductor Manager.

If the {@link #Index(Manager) Manager is present} in the data model it is {@link Manager#Close() closed} and it's entry removed from the data model.

N.B.: If the Manager is not present in data model it is not closed.

@param manager A Conductor Manager. @return true if the Manager was present in the data model and has been closed; false if the Manager was not present. */ public boolean Close ( Manager manager ) { int row = Index (manager); if (row >= 0) { manager.Close (); Managers.set (row, null); return true; } return false; } /*============================================================================== Manipulators */ /** Remove a data model row entry.

@param row A data model row index. @return The Conductor identity that was removed. This will be null if the row index is invalid. */ public Message Remove ( int row ) { Message identity = Identity (row); if (identity != null) { // Notify state change listeners before the row is removed. Conductor_Table_Model_Event event = Notify_Deleting (row); // Remove the row. Remove_at (row); // Notify table view listeners after the row is removed. Notify_Table_Change_Listeners (event); } return identity; } /** Remove a data model entry associated with a Conductor Identity.

@param identity A Conductor identity Message. If null nothing is done and false is returned. @return true if the identity was present and removed; false otherwise. */ public boolean Remove ( Message identity ) {return Remove (Index (identity)) != null;} /** Remove all Conductors at a Theater location.

Each data model entry {@link #Next_Index(String, int) found for the Theater location} is {@link #Remove_at(int) removed}. If any row was removed a change notification event for the entire table will be sent to the state change listeners. This event will also be sent to the table change listeners if non-contiguous ranges of rows were deleted; otherwise a {@link #Notify_Deleted(int, int) deleted} event will be delivered for only the rows removed.

@param theater_location The String specifying a Theater location (does not need to be fully qualified). @return true if at least one row was removed; false otherwise. @see #Next_Index(String, int) */ public boolean Remove ( String theater_location ) { int first_row = -1, last_row = -1, index = -1; while((index = Next_Index (theater_location, index)) >= 0) { Remove_at (index); if (first_row < 0) // Initialize the delete row indices. first_row = last_row = index; else if (last_row >= 0) { // Contigous range so far. if (first_row == index) // Contiguous row removal. ++last_row; else // Non-contiguous removal. last_row = -1; } /* Decrement the index so the Next_Index search will begin with the row following the one just removed. */ --index; } if (first_row >= 0) { // At least one row was removed. Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this); if (last_row >= 0) // Contiguous range removed. Notify the table listeners. Notify_Deleted (first_row, last_row); else // Non-contiguous ranges removed. Notify the table listeners. Notify_Table_Change_Listeners (event); // Notify the state change listeners. Notify_State_Change_Listeners (event); return true; } return false; } /** Clear the data model of all entries.

If the data model contians any {@link #getRowCount() rows} all are (@link #Remove_at(int) removed} and a {#link Notify_Table_Changed() table changed notification} is sent to all listeners. */ public void Clear () { int row = getRowCount (); if (row > 0) { while (--row >= 0) Remove_at (row); Notify_Table_Changed (); } } /** Remove a data model row entry.

N.B.: This method should only be used to remove a data model row by a method that will do the expected table change notification.

Any Manager and Remote_Theater Management for the row is {@link #Close_Management(int) closed}. Then the Manager, Mangement and Identity entries are removed from the data model.

N.B.: No data model listener notification is done.

@param row A data model row index. @return The Conductor identity Message for the row that was removed. This will be null if the row index is not valid. */ protected Message Remove_at ( int row ) { Message identity = Identity (row); if (identity != null) { Close_Management (row); Identities.remove (row); Managements.remove (row); Managers.remove (row); } return identity; } /** Close the Remote_Theater and Manager at a row index.

The Remote_Theater Management for the row is {@link #Close_Management(int) closed}. If this resulted in a Conductor {@link #Processing_State(int) processing state} change a {@link #Notify_State_Changed(int, int) state change notification} is sent to all data model listeners.

@param row A data model row index. */ public void Close ( int row ) { int previous_state = Close_Management (row); if (previous_state != UNCHANGED_STATE) Notify_State_Changed (row, previous_state); } /** Close a Theater.

The Remote_Theater Management {@link #Next_Index(String, int) found at the Theater location} is {@link #Close_Management(int) closed}. If any of these closures resulted in a Conductor {@link #Processing_State(int) processing state} change a {@link #Notify_Table_Changed() state change notification} is sent to all data model listeners.

@param theater_location A String specifying the Theater location (does not need to be fully qualified). If null or the emtpy String nothing is done. @return true if any closures resulted in a Conductor processing state change; false otherwise. @see #Next_Index(String, int) */ public boolean Close ( String theater_location ) { boolean changed = false; int index = -1; while ((index = Next_Index (theater_location, index)) >= 0) if (Close_Management (index) != UNCHANGED_STATE) changed = true; if (changed) Notify_Table_Changed (); return changed; } /** Close all Managers and Remote_Theater Managements.

Each {@link #Close_Manager(int) Manager is closed} and each {@link #Close_Management(int) Remote_Theater Management is closed}. If any of these closures resulted in a Conductor {@link #Processing_State(int) processing state} change a {@link #Notify_Table_Changed() state change notification} is sent to all data model listeners.

@return true if any closures resulted in a Conductor processing state change; false otherwise. */ public boolean Close_All () { boolean changed = false; int index = Managers.size (); while (--index >= 0) { Close_Manager (index); if (Close_Management (index) != UNCHANGED_STATE) changed = true; } if (changed) Notify_Table_Changed (); return changed; } /*============================================================================== Event Notification */ /** Notify all listeners that a data model row has been inserted.

Listeners are sent an {@link TableModelEvent#INSERT} Conductor_Table_Model_Event that specifies the row.

@param row The data model row that was inserted. */ protected void Notify_Inserted ( int row ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT); Notify_State_Change_Listeners (event); fireTableChanged (event); } /** Notify all listeners that a data model row has been updated.

Listeners are sent an {@link TableModelEvent#UPDATE} Conductor_Table_Model_Event that specifies the row.

@param row The data model row that was updated. */ protected void Notify_Updated ( int row ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE); Notify_State_Change_Listeners (event); fireTableChanged (event); } /** Notify all listeners that a continguous range of data model rows have been updated.

Listeners are sent an {@link TableModelEvent#UPDATE} Conductor_Table_Model_Event that specifies the range from first_row to last_row, inclusive.

@param first_row The first data model row that was updated. @param last_row The last data model row that was updated. */ protected void Notify_Updated ( int first_row, int last_row ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, first_row, last_row, TableModelEvent.ALL_COLUMNS, TableModelEvent.UPDATE); Notify_State_Change_Listeners (event); fireTableChanged (event); } /** Notify all listeners that a Conductor processing state changed.

Listeners are sent an {@link TableModelEvent#UPDATE} Conductor_Table_Model_Event that specifies the row and the processing state.

@param row The data model row that has changed. @param processing_state The previous processing state. The {@link #Processing_State(Message) current processing state} is contained in the Conductor {@link #Identity(int) identity} for the row. */ protected void Notify_State_Changed ( int row, int processing_state ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, row, row, CONDUCTORS_COLUMN, TableModelEvent.UPDATE) .Processing_State (processing_state); Notify_State_Change_Listeners (event); fireTableChanged (event); } /** Notify table change listeners that a data model row has been deleted.

Listeners are sent a {@link TableModelEvent#DELETE} Conductor_Table_Model_Event that specifies the row. N.B.: The {@link #Add_State_Change_Listener(TableModelListener) processing state change listeners} are not notified. They should have been sent a pre-deletion {@link #Notify_Deleting(int) deleting} notification of the same event.

@param row The data model row that has been deleted. */ protected void Notify_Deleted ( int row ) {fireTableChanged (new Conductor_Table_Model_Event (this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));} /** Notify table change listeners that a data model row range has been deleted.

Listeners are sent a {@link TableModelEvent#DELETE} Conductor_Table_Model_Event that specifies the range from first_row to last_row, inclusive. N.B.: The {@link #Add_State_Change_Listener(TableModelListener) processing state change listeners} are not notified. They should have been sent a pre-deletion {@link #Notify_Deleting(int) deleting} notification of the same event.

@param first_row The first data model row that has been deleted. @param last_row The last data model row that has been deleted. */ protected void Notify_Deleted ( int first_row, int last_row ) {fireTableChanged (new Conductor_Table_Model_Event (this, first_row, last_row, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE));} /** Notify all listeners that the data model content has changed.

This notification is used when the change to the data model can not be localized to a contiguous range of rows. All listeners are sent an {@link TableModelEvent#UPDATE} Conductor_Table_Model_Event that specifies the maximum row value range (0 - Integer.MAX_VALUE}. */ protected void Notify_Table_Changed () { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this); Notify_State_Change_Listeners (event); fireTableChanged (event); } /** Send a change notification to the table change listeners.

All {@link AbstractTableModel#addTableModelListener(TableModelListener) table model listeners} are notified of the event. N.B.: Only table model listeners are notified; the {@link #Add_State_Change_Listener(TableModelListener) processing state change listeners} are not notified. Use the {@link #Notify_State_Change_Listeners(Conductor_Table_Model_Event)} to notify only the processing state change listeners.

@param event The Conductor_Table_Model_Event to be delivered. */ protected void Notify_Table_Change_Listeners ( Conductor_Table_Model_Event event ) {fireTableChanged (event);} /*------------------------------------------------------------------------------ State Change Listeners */ /** Add a listener for Conductor processing state change events.

These listeners receive notification whenever one or more Conductor identities in the data model may have a changed {@link #Processing_State(Message) processing state}.

State change listeners are distinct from {@link AbstractTableModel#addTableModelListener(TableModelListener) table model listeners}. When appropriate, both sets of listeners will be notified of a data model change event. At other times only one or the other set of listeners will be sent a notification. A listener should register to receive processing state change notifications when that is all it is interested in knowing aobut. A listener should register to receive table change notifications whien it is interested in any changes, including processing state changes.

@param listener A TableModelListener. @return This Conductor_Table_Model. */ public Conductor_Table_Model Add_State_Change_Listener ( TableModelListener listener ) { if (listener != null && ! State_Change_Listeners.contains (listener)) State_Change_Listeners.add (listener); return this; } /** Remove a listener for Conductor processing state change events.

@param listener A TableModelListener. @return true if the listener was removed; false if it was not registered. @see #Add_State_Change_Listener(TableModelListener) */ public boolean Remove_State_Change_Listener ( TableModelListener listener ) {return State_Change_Listeners.remove (listener);} /** Send a change notification to the processing state change listeners.

All {@link #Add_State_Change_Listener(TableModelListener) state change listeners} are notified of the event. N.B.: Only processing state change listeners are notified; the {@link AbstractTableModel#addTableModelListener(TableModelListener) table model listeners} are not notified. Use the {@link #Notify_Table_Change_Listeners(Conductor_Table_Model_Event)} to notify only the table model listeners of an event.

@param event The Conductor_Table_Model_Event to be delivered. */ protected void Notify_State_Change_Listeners ( Conductor_Table_Model_Event event ) { int index = -1, size = State_Change_Listeners.size (); while (++index < size) State_Change_Listeners.get (index).tableChanged (event); } /** Notify processing state change listeners that a data model is about to be deleted.

Listeners are sent a {@link TableModelEvent#DELETE} Conductor_Table_Model_Event that specifies the row. N.B.: The {@link AbstractTableModel#addTableModelListener(TableModelListener) table change listeners} are not notified. They should be sent a post-deletion {@link #Notify_Deleted(int) deleted} notification of the same event.

@param row The data model row that will be deleted. @return The Conductor_Table_Model_Event that was delivered. */ protected Conductor_Table_Model_Event Notify_Deleting ( int row ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, row, row, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE); Notify_State_Change_Listeners (event); return event; } /** Notify table change listeners that a data model row range is about to be deleted.

Listeners are sent a {@link TableModelEvent#DELETE} Conductor_Table_Model_Event that specifies the range from first_row to last_row, inclusive. N.B.: The {@link AbstractTableModel#addTableModelListener(TableModelListener) table change listeners} are not notified. They should be sent a post-deletion {@link #Notify_Deleted(int) deleted} notification of the same event.

@param first_row The first data model row that is about to be deleted. @param last_row The last data model row that is about to be deleted. @return The Conductor_Table_Model_Event that was delivered. */ protected Conductor_Table_Model_Event Notify_Deleting ( int first_row, int last_row ) { Conductor_Table_Model_Event event = new Conductor_Table_Model_Event (this, first_row, last_row, TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE); Notify_State_Change_Listeners (event); return event; } /*============================================================================== Processing_Listener */ private Vector Processing_Event_Queue = new Vector (); /** Event handler for Conductor Processing_Events.

N.B.: This method is public as a side effect of implementing the Processing_Listener interface. It should not be used directly.

If the incoming event contains a Conductor processing event change the event is enqueued and a Runnable placed on the Swing event handling queue for disposition of the event.

The event disposition method pulls the next event from the front of the event queue. The data model index for the Remote_Theater Management source of the event is obtained and used to obtain the associated Conductor identity. The identity provides the previous processing state and is set with the new state. Then all {@link #Add_State_Change_Listener(TableModelListener) processing state change listeners} are {@link #Notify_State_Changed(int, int) notified of the state change}.

@param event A Processing_Event. @see Processing_Event */ public void Processing_Event_Occurred ( Processing_Event event ) { if (event.Changes.Processing_State () == 0) return; if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (">-< Processing_Event_Occurred"); synchronized (Processing_Event_Queue) {Processing_Event_Queue.add (event);} SwingUtilities.invokeLater (new Runnable () {public void run () {Event_Disposition ();}}); } private void Event_Disposition () { if (Processing_Event_Queue.isEmpty ()) return; if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (">>> Event_Disposition"); Processing_Event event; synchronized (Processing_Event_Queue) /* >>> WARNING <<< No check is being done for an empty queue because Message_Disposition is run from the event queue after a Message_Delivered enqueues the delivered Message, and only one Message is removed from the queue each time Message_Disposition is run. If this one-to-one Message queuing relationship between Message_Delivered and Message_Disposition is ever changed an empty queue check must be added. */ {event = (Processing_Event)Processing_Event_Queue.remove (0);} int index = Index ((Remote_Theater)event.getSource ()); if (index < 0) // Shouldn't happen if properly deregistered on Management removal. return; // Update the processing state. Message identity = Identities.get (index); if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" Notify_State_Changed to " + event.Changes.Processing_State () + NL +"for" + NL + identity); int processing_state = Processing_State (identity); Processing_State (identity, event.Changes.Processing_State ()); Notify_State_Changed (index, processing_state); if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println ("<<< Event_Disposition"); } /*============================================================================== Utilities */ /** Get the location of a Conductor from its identity.

If the {@link #THEATER_LOCATION_PARAMETER_NAME} is not present in the identity the value of the {@link #CONDUCTOR_ID_PARAMETER_NAME} will be used, without any PID suffix, to produce a {@link Theater#Full_Location(String) fully qualified location}.

@param identity A Conductor {@link Conductor#Identity() identity} Message. @return A String providing the Theater location of the Conductor. This will be null if the identity is null or neither of the expected parameters is present in the identity. */ public static String Theater_Location ( Message identity ) { String location = null; if (identity != null) { if ((location = identity.Get (THEATER_LOCATION_PARAMETER_NAME)) == null) { location = identity.Get (CONDUCTOR_ID_PARAMETER_NAME); int index = location.indexOf (':'); if (index > 0) location = location.substring (0, index); location = Theater.Full_Location (location); } } return location; } /** Get the name of a Conductor from its identity.

The value of the {@link #PIPELINE_PARAMETER_NAME} will be returned. N.B.: A Conductor's name is not necessarily the same as its {@link #Pipeline(Message) pipeline name}.

@param identity A Conductor {@link Conductor#Identity() identity} Message. @return A String providing the name of the Conductor. This will be null if the identity is null or neither of the expected parameters is present in the identity. */ public static String Conductor_Name ( Message identity ) { if (identity != null) { if (identity.Name ().equals (PIRL.PVL.Parser.CONTAINER_NAME) || identity.Name ().equals (Theater.IDENTITY_ACTION)) return identity.Get (PIPELINE_PARAMETER_NAME); return identity.Name (); } return null; } /** Get the name of a Conductor pipeline from its identity.

The {@link #PIPELINE_PARAMETER_NAME} value is obtained.

@param identity A Conductor {@link Conductor#Identity() identity} Message. @return A String providing the name of the Conductor pipeline. This will be null if the identity is null or the required parameter is not present in the identity. */ public static String Pipeline ( Message identity ) { String name = null; if (identity != null) name = identity.Get (PIPELINE_PARAMETER_NAME); return name; } /** Get a Conductor identification from its identity.

The {@link #CONDUCTOR_ID_PARAMETER_NAME} value is obtained.

@param identity A Conductor {@link Conductor#Identity() identity} Message. @return A String providing the identification of the Conductor pipeline. This will be null if the identity is null or the required parameter is not present in the identity. */ public static String Identification ( Message identity ) { String id = null; if (identity != null) id = identity.Get (CONDUCTOR_ID_PARAMETER_NAME); return id; } /** Get the processing state of a Conductor.

The processing state is the value of the {@link #PROCESSING_STATE_PARAMETER_NAME} from the identity. If this parameter is not present zero will be returned. The expected processing state values are:

{@link Conductor#RUNNING}
Source records are being processing.
{@link Conductor#POLLING}
No source records are currently available for processing; the Conductor is polling for new source records to process.
{@link Conductor#RUN_TO_WAIT}
When processing of the current source record completes Conductor will go into the waiting state.
{@link Conductor#WAITING}
The Conductor is waiting to be told to being processing.
{@link Conductor#HALTED}
A problem condition caused the Conductor to halt processing. The problem may be the result of the maximum number of sequential source records processing procedure failures having occured, a database access failure, or some other system error.
0 - Unknown
A processing state has not yet been obtained from the Conductor.
The WAITING and HALTED state codes are negative; all others are positive.

@param identity A Conductor {@link Conductor#Identity() identity} Message. If null, zero is returned. @return A Conductor processing state value, or zero if none is available. */ public static int Processing_State ( Message identity ) { int processing_state = 0; if (identity != null) { try {processing_state = Integer.parseInt (identity.Get (PROCESSING_STATE_PARAMETER_NAME));} catch (NumberFormatException exception) {} } return processing_state; } /** Set the processing state of a Conductor.

N.B.: This method should only be used to set a new processing state by a method that does the neccessary state checks and table view update notification.

The Conductor identity has the {@link #PROCESSING_STATE_PARAMETER_NAME} set to the processing state value.

@param identity A Conductor {@link Conductor#Identity() identity} Message. Must not be null. @param processing_state A processing state value. @see #Processing_State(Message) */ protected static void Processing_State ( Message identity, int processing_state ) {identity.Set (PROCESSING_STATE_PARAMETER_NAME, processing_state, 0);} } pirl-2.3.8/PIRL/Conductor/Maestro/tests/0000755000175000017500000000000012052546525017634 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Maestro/tests/test_Error_Report.java0000644000175000017500000000447211742733134024170 0ustar mathieumathieu/* test_Error_Report CVS ID: test_Error_Report.java,v 1.4 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Conductor.Maestro.Error_Report; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class test_Error_Report extends JFrame { private static test_Error_Report Test; public test_Error_Report () { super ("test_Error_Report"); JPanel panel = new JPanel (); JButton button = new JButton ("Error Report"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) { Exception exception = new IllegalArgumentException ("Exception from test_Error_Report"); new Error_Report ( "Error_Report test", "This is a test of the Error_Report dialog." + '\n' + '\n' + "The scroll pane, below, should contain a stack trace" + '\n' + "for an IllegalArgumentException.", exception, Test ); }}); panel.add (button); getContentPane ().add (panel, BorderLayout.CENTER); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } public static void main (String[] args) { System.out.println ("*** test_Error_Report" + '\n' + Error_Report.ID); Test = new test_Error_Report (); Test.setLocation (250, 300); Test.setPreferredSize (new Dimension (100, 50)); Test.setVisible (true); } } pirl-2.3.8/PIRL/Conductor/Maestro/tests/test_Profile.java0000644000175000017500000005261711742733134023150 0ustar mathieumathieu/* test_Profile Unit test for the PIRL.Conductor.Maestro.Profile class. PIRL CVS ID: test_Profile.java,v 1.15 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import java.io.File; import java.io.FileWriter; import java.util.List; import java.util.Vector; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Conductor.Maestro.Profile; import PIRL.Conductor.Maestro.Conductor_Definition; import PIRL.PVL.Parameter; import PIRL.PVL.PVL_Exception; import PIRL.PVL.Value; import PIRL.Messenger.Message; import PIRL.Utilities.Checker; public class test_Profile { /** The name of the test profile file we'll be using. This file will be created during the testing, in the directory from which the testing is conducted. */ private static final String PROFILE_FILENAME = "test_Profile.cmp"; /** New Line seperator for this operating system. */ private static String NL = System.getProperty ("line.separator") ; private static Checker checker = new Checker (); /** Application main. */ public static void main ( String[] arguments ) { System.out.println ("*** test_Profile: " + Profile.ID); int exit_status = 0; if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; try { Profile profile; Conductor_Definition conductor; Vector conductors, theater, theaters; Message message; Parameter param; Configuration config; String expected; Exception expected_exception, obtained_exception; profile = new Profile (); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add (null); theater.add (0); profile.Add_Theater_Definition ("host_1"); theaters.add (theater); if (! checker.Check ("Add_Theater_Definition (String (\"host_1\"))", theaters, profile.Theater_Definitions ())) Report (1); profile = new Profile (); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (1); profile.Validate_Theater_Definition (theater); profile.Add_Theater_Definition (theater); theaters.add (theater); if (! checker.Check ("Add_Theater_Definition (Vector (\"host_1:4144\", \"pipeline_1\", 1))", theaters, profile.Theater_Definitions ())) Report (1); profile = new Profile (); conductors = new Vector (); conductor = new Conductor_Definition ("pipeline_1"); conductors.add (conductor); profile.Add_Conductor_Definition (conductor); if (! checker.Check ("Add_Conductor_Definition (Conductor_Definition (\"pipeline_1\"))", conductors, profile.Conductor_Definitions ())) Report (1); profile = new Profile (); theater = new Vector (); theater.add ("host_2:4144"); theater.add ("pipeline_2"); theater.add (1); theaters.add (theater); profile.Validate_Theater_Definitions (theaters); profile.Add_Theater_Definitions (theaters); if (! checker.Check ("Add_Theater_Definitions (Vector (" + "Vector (\"host_1:4144\", \"pipeline_1\", 1), " + "Vector (\"host_2:4144\", \"pipeline_2\", 1)))", theaters, profile.Theater_Definitions ())) Report (1); profile = new Profile (); message = new Message () .Set ("Pipeline", "pipeline_1"); profile.Add_Conductor_Definition (message); if (! checker.Check ("Add_Conductor_Definition (Message ().Set (\"Pipeline\", \"pipeline_1\"))", conductors, profile.Conductor_Definitions ())) Report (1); profile = new Profile (); conductor = new Conductor_Definition ("pipeline_2"); conductors.add (conductor); profile.Add_Conductor_Definitions (conductors); if (! checker.Check ("Add_Conductor_Definitions (Vector " + "(Conductor_Definition (\"pipeline_1\"), " + "Conductor_Definition (\"pipeline_2\"))", conductors, profile.Conductor_Definitions ())) Report (1); profile = new Profile (); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (1); theaters.add (theater); conductors = new Vector (); conductor = new Conductor_Definition ("pipeline_1"); conductors.add (conductor); profile.Add ("host_1", conductor); if (! checker.Check ("Theaters via Add (String (\"host_1\"), " + "Conductor_Definition (\"pipeline_1\")", theaters, profile.Theater_Definitions ())) Report (1); if (! checker.Check ("Conductors via Add (String (\"host_1\"), " + "Conductor_Definition (\"pipeline_1\")", conductors, profile.Conductor_Definitions ())) Report (1); // Turn theater into a canonical theater definition theater.set (1, conductor); if (! checker.Check ("Canonical Theaters via Add (String (\"host_1\"), " + "Conductor_Definition (\"pipeline_1\")", theaters, profile.Canonical_Theater_Definitions ())) Report (1); profile = new Profile (); // Turn theater back into a non-canonical theater definition theater.set (1, "pipeline_1"); profile.Add (theaters, conductors); if (! checker.Check ("Theaters via Add (Vector (\"host_1:4144\", \"pipeline_1\", 1), " + "Vector (Conductor_Definition (\"pipeline_1\"))", theaters, profile.Theater_Definitions ())) Report (1); if (! checker.Check ("Conductors via Add (Vector (\"host_1:4144\", \"pipeline_1\", 1), " + "Vector (Conductor_Definition (\"pipeline_1\"))", conductors, profile.Conductor_Definitions ())) Report (1); // Turn theater into a canonical theater definition theater.set (1, conductor); if (! checker.Check ("Canonical Theaters via Add (Vector (\"host_1:4144\", \"pipeline_1\", 1), " + "Vector (Conductor_Definition (\"pipeline_1\"))", theaters, profile.Canonical_Theater_Definitions ())) Report (1); profile = new Profile (); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (1); theaters.add (theater); theater = new Vector (); theater.add ("host_2:4144"); theater.add ("pipeline_2"); theater.add (1); theaters.add (theater); theater = new Vector (); theater.add ("host_3:4144"); theater.add ("pipeline_3"); theater.add (1); theaters.add (theater); profile.Validate_Theater_Definitions (theaters); profile.Add_Theater_Definitions (theaters); conductors = new Vector (); conductor = new Conductor_Definition ("pipeline_1"); conductors.add (conductor); conductor = new Conductor_Definition ("pipeline_2"); conductors.add (conductor); conductor = new Conductor_Definition ("pipeline_3"); conductors.add (conductor); profile.Add_Conductor_Definitions (conductors); if (! checker.Check ("Total_Theater_Definitions ()", 3, profile.Total_Theater_Definitions ())) Report (1); if (! checker.Check ("Total_Conductor_Definitions ()", 3, profile.Total_Conductor_Definitions ())) Report (1); if (! checker.Check ("Theater_Definition_index " + "(profile.Theater_Definitions (), \"host_2\", \"pipeline_2\")", 1, profile.Theater_Definition_Index (profile.Theater_Definitions (), "host_2", "pipeline_2"))) Report (1); if (! checker.Check ("Theater_Definition_index " + "(profile.Theater_Definitions (), \"undefined\", \"undefined\")", -1, profile.Theater_Definition_Index (profile.Theater_Definitions (), "undefined", "undefined"))) Report (1); if (! checker.Check ("Next_Theater_Index (\"host_2:4144\", -1)", 1, profile.Next_Theater_Index ("host_2:4144", -1))) Report (1); if (! checker.Check ("Next_Theater_Index (\"undefined\", -1)", -1, profile.Next_Theater_Index ("undefined", -1))) Report (1); if (! checker.Check ("Theater_Definition (1)", theaters.get (1), profile.Theater_Definition (1))) Report (1); if (! checker.Check ("Theater_Definition (-1)", null, profile.Theater_Definition (-1))) Report (1); if (! checker.Check ("Theater_Definition (\"host_2\", \"pipeline_2\")", theaters.get (1), profile.Theater_Definition ("host_2", "pipeline_2"))) Report (1); if (! checker.Check ("Theater_Definition (\"undefined\", \"undefined\")", null, profile.Theater_Definition ("undefined", "undefined"))) Report (1); if (! checker.Check ("Contains_Conductor_Definition (Conductor_Definition (\"pipeline_2\"))", true, profile.Contains_Conductor_Definition (new Conductor_Definition ("pipeline_2")))) Report (1); if (! checker.Check ("Contains_Conductor_Definition (Conductor_Definition (\"undefined\"))", false, profile.Contains_Conductor_Definition (new Conductor_Definition ("undefined")))) Report (1); if (! checker.Check ("Conductor_Definition_index " + "(profile.Conductor_Definitions (), \"pipeline_2\")", 1, profile.Conductor_Definition_Index (profile.Conductor_Definitions (), "pipeline_2"))) Report (1); if (! checker.Check ("Conductor_Definition_index " + "(profile.Conductor_Definitions (), \"undefined\")", -1, profile.Conductor_Definition_Index (profile.Conductor_Definitions (), "undefined"))) Report (1); if (! checker.Check ("Conductor_Definition (\"pipeline_2\")", conductors.get (1), profile.Conductor_Definition ("pipeline_2"))) Report (1); if (! checker.Check ("Conductor_Definition (\"undefined\")", null, profile.Conductor_Definition ("undefined"))) Report (1); if (! checker.Check ("Contains_Theater_Definition (\"host_2:4144\")", true, profile.Contains_Theater_Definition ("host_2:4144"))) Report (1); if (! checker.Check ("Contains_Theater_Definition (\"undefined\")", false, profile.Contains_Theater_Definition ("undefined"))) Report (1); profile.Remove_Theater_Definition (1); theaters.remove (1); if (! checker.Check ("Remove_Conductor_Definition (1)", theaters, profile.Theater_Definitions ())) Report (1); profile.Remove_Theater_Definition ("host_1", "pipeline_1"); theaters.remove (0); if (! checker.Check ("Remove_Conductor_Definition (\"host_1\", \"pipeline_1\")", theaters, profile.Theater_Definitions ())) Report (1); profile.Remove_Conductor_Definition (1); conductors.remove (1); if (! checker.Check ("Remove_Conductor_Definition (1)", conductors, profile.Conductor_Definitions ())) Report (1); profile.Remove_Conductor_Definition ("pipeline_1"); conductors.remove (0); if (! checker.Check ("Remove_Conductor_Definition (\"pipeline_1\")", conductors, profile.Conductor_Definitions ())) Report (1); profile.Clear_Theater_Definitions (); if (! checker.Check ("Clear_Theater_Definitions ()", new Vector (), profile.Theater_Definitions ())) Report (1); profile.Clear_Conductor_Definitions (); if (! checker.Check ("Clear_Conductor_Definitions ()", new Vector (), profile.Conductor_Definitions ())) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (1); theaters.add (theater); theater = new Vector (); theater.add ("host_2:4144"); theater.add ("pipeline_2"); theater.add (2); theaters.add (theater); param = new Parameter ("Theaters"); Value.Default_Array_Type (Value.SEQUENCE); param.Value (theaters); config.Add (param); profile.Validate_Theater_Definitions (theaters); profile.Add_Theater_Definitions (theaters); conductors = new Vector (); conductor = new Conductor_Definition ("pipeline_1"); conductors.add (conductor); config.add (conductor); conductor = new Conductor_Definition ("pipeline_2"); conductors.add (conductor); config.add (conductor); profile.Add_Conductor_Definitions (conductors); if (! checker.Check ("Configuration ()", config, profile.Configuration ())) { System.out.println (" expected Configuration -" + NL + config.Description () + NL +" obtained Configuration -" + NL + profile.Configuration ().Description ()); Report (1); } profile = new Profile (); profile.Read ( new Configuration (config)); if (! checker.Check ("Read (Configuration without Theaters in Conductor Definitions)", config, profile.Configuration ())) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); conductor = new Conductor_Definition ("pipeline_1"); conductor.Set (profile.THEATERS_PARAMETER_NAME, "host_1"); config.add (conductor); profile.Read (new Configuration (config)); config = new Configuration ((Parameter)null); config.Name ("Profile"); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (1); theaters.add (theater); param = new Parameter ("Theaters"); param.Value (theaters); config.Add (param); conductor = new Conductor_Definition ("pipeline_1"); config.add (conductor); if (! checker.Check ("Read (Configuration with Theater string in Conductor Definitions)", config, profile.Configuration ())) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); conductor = new Conductor_Definition ("pipeline_1"); conductor.Set (profile.THEATERS_PARAMETER_NAME, theaters); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add (2); theaters.add (theater); conductor.Set (profile.THEATERS_PARAMETER_NAME, theaters); config.add (conductor); profile.Read (new Configuration (config)); config = new Configuration ((Parameter)null); config.Name ("Profile"); theaters = new Vector (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add (2); theaters.add (theater); param = new Parameter ("Theaters"); param.Value (theaters); config.Add (param); conductor = new Conductor_Definition ("pipeline_1"); conductors.add (conductor); config.add (conductor); if (! checker.Check ("Read (Configuration with Theater array in Conductor Definitions)", config, profile.Configuration ())) Report (1); if (! checker.Check ("Unprocessed () is null", null, profile.Unprocessed_Source_Parameters ())) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); conductor = new Conductor_Definition ("pipeline_1"); config.add (conductor); conductor.Set (profile.THEATERS_PARAMETER_NAME, "host_1"); conductor = new Conductor_Definition ("pipeline_2"); theaters = new Vector (); theater = new Vector (); theater.add ("host_2:4144"); theater.add (2); theaters.add (theater); conductor.Set (profile.THEATERS_PARAMETER_NAME, theaters); config.add (conductor); config.add (new Parameter ("token")); param = new Parameter ("assignment"); param.Value ("value"); config.Add (param); profile.Read (new Configuration (config)); config = new Configuration ((Parameter)null); config.Name ("Profile"); config.add (new Parameter ("token")); param = new Parameter ("assignment"); param.Value ("value"); config.Add (param); if (! checker.Check ("Unprocessed () is not null", config, profile.Unprocessed_Source_Parameters ())) Report (1); expected = "# Profile" + NL + "Theaters = " + NL + " (" + NL + " (host_1:4144, pipeline_1, 1)," + NL + " (host_2:4144, pipeline_2, 2))" + NL + "GROUP = pipeline_1" + NL + " Pipeline = pipeline_1" + NL + " Configuration = Conductor.conf" + NL + "END_GROUP" + NL + "GROUP = pipeline_2" + NL + " Pipeline = pipeline_2" + NL + " Configuration = Conductor.conf" + NL + "END_GROUP" + NL + "END" + NL; if (! checker.Check ("toString ()", expected, profile.toString ())) Report (1); profile = new Profile (); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Validate_Theater_Definition (theater);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Validate_Theater_Definition (theater with < 3 entries)", expected_exception, obtained_exception)) Report (1); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add ("extra"); theater.add (1); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Validate_Theater_Definition (theater);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Validate_Theater_Definition (theater with > 3 entries)", expected_exception, obtained_exception)) Report (1); theater = new Vector (); theater.add (1); theater.add ("pipeline_1"); theater.add (1); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Validate_Theater_Definition (theater);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Validate_Theater_Definition (theater with first not string)", expected_exception, obtained_exception)) Report (1); theater = new Vector (); theater.add ("host_1:4144"); theater.add ("pipeline_1"); theater.add ("extra"); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Validate_Theater_Definition (theater);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Validate_Theater_Definition (theater with 3 strings)", expected_exception, obtained_exception)) Report (1); theater = new Vector (); theater.add ("host_1:4144"); theater.add (1); theater.add (1); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Validate_Theater_Definition (theater);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Validate_Theater_Definition (theater with 1 string and 2 numbers)", expected_exception, obtained_exception)) Report (1); profile = new Profile (); conductor = new Conductor_Definition ("pipeline_1"); conductor.Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, "configuration_1"); profile.Add_Conductor_Definition (conductor); expected_exception = null; obtained_exception = null; try {profile.Add_Conductor_Definition (conductor);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("No Exception - Add_Conductor_Definition (Conductor_Definition that was already added but hasn't changed)", expected_exception, obtained_exception)) Report (1); profile = new Profile (); conductor = new Conductor_Definition ("pipeline_1"); conductor.Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, "configuration_1"); profile.Add_Conductor_Definition (conductor); conductor.Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, "configuration_2"); expected_exception = new IllegalArgumentException (); obtained_exception = null; try {profile.Add_Conductor_Definition (conductor);} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("IllegalArgumentException - Add_Conductor_Definition (Conductor_Definition that was already added and has different values)", expected_exception, obtained_exception)) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); conductor = new Conductor_Definition ("pipeline_1"); config.add (conductor); conductor.Set (profile.THEATERS_PARAMETER_NAME, "host_1"); conductor = new Conductor_Definition ("pipeline_2"); config.add (conductor); theaters = new Vector (); theater = new Vector (); theater.add ("host_2:4144"); Vector v = new Vector (); v.add ("1"); v.add ("2"); theater.add (v); theaters.add (theater); conductor.Set (profile.THEATERS_PARAMETER_NAME, theaters); expected_exception = new Configuration_Exception (); obtained_exception = null; try {profile.Read (new Configuration (config));} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("Configuration_Exception - Read (Configuration with Conductor Definition that has array in Theater definition)", expected_exception, obtained_exception)) Report (1); profile = new Profile (); config = new Configuration ((Parameter)null); config.Name ("Profile"); conductor = new Conductor_Definition ("pipeline_1"); config.add (conductor); conductor.Set (profile.THEATERS_PARAMETER_NAME, "host_1"); conductor = new Conductor_Definition ("pipeline_2"); config.add (conductor); theaters = new Vector (); theater = new Vector (); theater.add ("host_2:4144"); theater.add ("string"); theaters.add (theater); conductor.Set (profile.THEATERS_PARAMETER_NAME, theaters); expected_exception = new Configuration_Exception (); obtained_exception = null; try {profile.Read (new Configuration (config));} catch (Exception exception) {obtained_exception = exception;} if (! checker.Check ("Configuration_Exception - Read (Configuration with Conductor Definition that has two strings for Theater definition", expected_exception, obtained_exception)) Report (1); } catch (Exception exception) { System.out.println (exception); exception.printStackTrace (System.out); exit_status = 1; } Report (exit_status); } private static void Report ( int exit_status ) { System.out.println ("\n" + "Checks: " + checker.Checks_Total + '\n' + "Passed: " + checker.Checks_Passed); if (exit_status == 0 && checker.Checks_Total == checker.Checks_Passed) new File (PROFILE_FILENAME).delete (); System.exit (exit_status); } } pirl-2.3.8/PIRL/Conductor/Maestro/tests/Makefile0000644000175000017500000000060611061073617021272 0ustar mathieumathieu# Makefile for Java classes # PIRL CVS ID: Makefile,v 1.3 2008/09/08 00:53:35 castalia Exp JPATH ?= .:../../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Profile_Checker.class \ test_Profile.class \ test_Conductor_Definition.class \ test_Error_Report.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Conductor/Maestro/tests/Profile_Checker.java0000644000175000017500000001002111742733134023514 0ustar mathieumathieu/* Profile_Checker PIRL CVS ID: Profile_Checker.java,v 1.4 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Conductor.Maestro.Profile; /** A Profile_Checker validates a Conductor matrix definition Profile.

@see Profile @author Michael Wendell, UA/HiROC @version 1.4 */ public class Profile_Checker { /** Success exit status (0). */ public static final int EXIT_SUCCESS = 0; /** Command line syntax problem exit status (1). */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** Profile error (2). */ public static final int EXIT_ERROR = 2; private static String NL = System.getProperty ("line.separator");; /*============================================================================== Application main */ /** Validate a Profile source. */ public static void main ( String args[] ) { System.out.println (Profile.ID); if (args.length == 0) Usage (); String source = null; boolean verbose = false; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].toUpperCase ().charAt (1)) { case 'I': // Input. if (++count == args.length || args[count].charAt(0) == '-') { System.out.println ("Missing profile source."); Usage (); } if (source != null && ! source.equals (args[count])) { System.out.println ("Multiple profile sources specified -" + NL + source + NL + "and" + NL + args[count]); Usage (); } source = args[count]; break; case 'V': // Verbose. verbose = true; break; default: System.out.println ("Unrecognized argument: " + args[count]); case 'H': // Help Usage (); } } else { // Input source. if (source != null && ! source.equals (args[count])) { System.out.println ("Multiple profile sources specified -" + NL + source + NL + "and" + NL + args[count]); Usage (); } source = args[count]; } } if (source == null) { System.out.println ("No profile source specified."); Usage (); } int status = EXIT_SUCCESS; try { Profile profile = new Profile (source); System.out.println ("Valid profile: " + source); if (verbose) System.out.println (profile); } /* CATCH THE SPECIFIC types of exception that might be thrown and give addequate error report. catch (Configuration_Exception exception) { status = EXIT_CONFIGURATION_PROBLEM; message = exception.getMessage (); } */ catch (Exception exception) { // set status and message System.out.println ("Invalid profile: " + source + '\n' + exception.getMessage ()); status = EXIT_ERROR; } System.exit (status); } /** Print usage statement. */ private static void Usage () { System.out.println ("Usage: Profile_Checker [-Input] " + NL +" The input source may be a local file pathname or a URL." + NL +" Options -" + NL +" -Verbose" + NL +" Lists the resultant profile." + NL +" Default: Only validity results and any errors are reported." + NL +" -Help" + NL ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Conductor/Maestro/tests/test_Conductor_Definition.java0000644000175000017500000002136111742733134025650 0ustar mathieumathieu/* test_Conductor_Definition PIRL CVS ID: test_Conductor_Definition.java,v 1.7 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Conductor.Maestro.Conductor_Definition; import PIRL.Messenger.Message; import PIRL.Utilities.Checker; public class test_Conductor_Definition { private static String NL = System.getProperty ("line.separator") ; private static Checker checker = new Checker (); public static void main ( String[] arguments ) { System.out.println ("*** test_Conductor_Definition" + NL + Conductor_Definition.ID + NL); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; int exit_status = 0; try { Conductor_Definition definition, new_definition; Message message; // Construction message = new Message (); message.Name ("definition"); definition = new Conductor_Definition (message); String expected = "GROUP = definition" + NL + " " + Conductor_Definition.PIPELINE_PARAMETER_NAME + " = definition" + NL + " " + Conductor_Definition.CONFIGURATION_PARAMETER_NAME + " = Conductor.conf" + NL + "END_GROUP"; if (! checker.Check ("new Conductor_Definition (\"definition\")", expected, new Conductor_Definition ("definition").Description ())) Report (1); message = new Message (); message.Name ("definition"); definition = new Conductor_Definition (message); if (! checker.Check ("new Conductor_Definition (new Message (\"definition\"))", expected, definition.Description ())) Report (1); new_definition = new Conductor_Definition (definition); if (! checker.Check ("Construct new_definition from definition", expected, new_definition.Description ())) Report (1); if (! checker.Check ("definition.Matches (new_definition))", true, definition.Matches (new_definition))) { System.out.println ("====> definition -" + NL + definition + NL +"====> new_definition -" + NL + new_definition); Report (1); } expected = "GROUP = definition" + NL + " " + Conductor_Definition.PIPELINE_PARAMETER_NAME + " = pipeline" + NL + " " + Conductor_Definition.CONFIGURATION_PARAMETER_NAME + " = configuration" + NL + " " + Conductor_Definition.SERVER_PARAMETER_NAME + " = server" + NL + " " + Conductor_Definition.CATALOG_PARAMETER_NAME + " = catalog" + NL + "END_GROUP"; message = new Message () .Set (Conductor_Definition.CATALOG_PARAMETER_NAME, "catalog") .Set (Conductor_Definition.SERVER_PARAMETER_NAME, "server") .Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, "configuration") .Set (Conductor_Definition.PIPELINE_PARAMETER_NAME, "pipeline"); message.Name ("definition"); definition = new Conductor_Definition (message); if (! checker.Check ("Parameter name ordering", expected, definition.Description ())) Report (1); message .Set ("Extra", "extra") .Set ("Added", "added"); definition = new Conductor_Definition (message); if (! checker.Check ("Irrelevant parameters removed.", expected, definition.Description ())) Report (1); Exception expected_exception = new IllegalArgumentException (), obtained_exception = null; try {new Conductor_Definition ((String)null);} catch (Exception exception) {obtained_exception = exception;} checker.Check ("IllegalArgumentException - new Conductor_Definition ((String)null)", expected_exception, obtained_exception); obtained_exception = null; try {new Conductor_Definition (new Message ());} catch (Exception exception) {obtained_exception = exception;} checker.Check ("IllegalArgumentException - new Conductor_Definition (new Message ())", expected_exception, obtained_exception); message = new Message () .Set (Conductor_Definition.PIPELINE_PARAMETER_NAME, "pipeline"); obtained_exception = null; try {new Conductor_Definition (message);} catch (Exception exception) {obtained_exception = exception;} checker.Check ("IllegalArgumentException - new Conductor_Definition (new Message ().Set (\"" + Conductor_Definition.PIPELINE_PARAMETER_NAME + "\", \"pipeline\"))", null, obtained_exception); // Matches definition = new Conductor_Definition ("pipeline"); message = new Message (); if (! checker.Check ("new Conductor_Definition (\"pipeline\").Matches new Message () = false", false, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } message.Name ("pipeline"); if (! checker.Check ("new Conductor_Definition (\"pipeline\").Matches message.Name (\"pipeline\")", true, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } message = new Message () .Set (Conductor_Definition.PIPELINE_PARAMETER_NAME, "pipeline"); if (! checker.Check ("definition.Matches with only \"" + Conductor_Definition.PIPELINE_PARAMETER_NAME + "\" parameter", true, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } message = new Message () .Set (Conductor_Definition.CATALOG_PARAMETER_NAME, "catalog") .Set (Conductor_Definition.SERVER_PARAMETER_NAME, "server") .Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, "configuration") .Set (Conductor_Definition.PIPELINE_PARAMETER_NAME, "pipeline"); message.Name ("pipeline"); definition = new Conductor_Definition (message); if (! checker.Check ("definition.Matches Message with all parameters", true, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } message .Set ("Extra", "extra") .Set ("Added", "added"); if (! checker.Check ("definition.Matches Message with extra parameters", true, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } message.Remove (Conductor_Definition.CONFIGURATION_PARAMETER_NAME); if (! checker.Check ("definition.Matches Message without \"" + Conductor_Definition.CONFIGURATION_PARAMETER_NAME + "\" parameter = false", false, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } definition .Set (Conductor_Definition.CONFIGURATION_PARAMETER_NAME, Conductor_Definition.DEFAULT_CONFIGURATION_FILENAME); if (! checker.Check ("definition with default configuration Matches Message without \"" + Conductor_Definition.CONFIGURATION_PARAMETER_NAME + "\" parameter = false", true, definition.Matches (message))) { System.out.println ("====> message -" + NL + definition + NL +"====> definition -" + NL + definition); Report (1); } // Start_Conductor_Message message = new Message (definition); message .Set (Conductor_Definition.ACTION_PARAMETER_NAME, Conductor_Definition.START_CONDUCTORS_ACTION, 0) .Name (definition.Name ()); checker.Check ("definition.Start_Conductor_Message (false, 0)", message.Description (), definition.Start_Conductor_Message (false, 0).Description ()); message .Set_Token (Conductor_Definition.WAIT_TO_START_PARAMETER_NAME); message .Set (Conductor_Definition.COUNT_PARAMETER_NAME, 2); checker.Check ("definition.Start_Conductor_Message (true, 2)", message.Description (), definition.Start_Conductor_Message (true, 2).Description ()); message .Remove (Conductor_Definition.WAIT_TO_START_PARAMETER_NAME); checker.Check ("definition.Start_Conductor_Message (true, 2)", message.Description (), definition.Start_Conductor_Message (false, 2).Description ()); } catch (Exception exception) { System.out.println (exception); exception.printStackTrace (System.out); exit_status = 1; } Report (exit_status); } private static void Report ( int exit_status ) { System.out.println ("\n" + "Checks: " + checker.Checks_Total + '\n' + "Passed: " + checker.Checks_Passed); System.exit (exit_status); } } pirl-2.3.8/PIRL/Conductor/Maestro/Theater_Protocol_Exception.java0000644000175000017500000001522511742733134024634 0ustar mathieumathieu/* Theater_Protocol_Exception PIRL CVS ID: Theater_Protocol_Exception.java,v 1.7 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import java.io.IOException; /** A Theater_Protocol_Exception is thrown when a Theater method call encounters a problem with the Stage_Manager protocol.

A Theater_Protocol_Exception may have a message, reason and cause. Usually the message is provided by the caller to descrbe the context of the exception and/or provide information that may be useful is solving the problem. A reason code is intended to indicate the category of the problem.

@author Bradford Castalia - UA/PIRL @version 1.7 @see Theater */ public class Theater_Protocol_Exception extends IOException { public static final String ID = "PIRL.Conductor.Maestro.Theater_Protocol_Exception (1.7 2012/04/16 06:04:12)"; /** The default {@link #Reason() reason} code indicating that categorization of the exception was not specified. */ public static final int UNSPECIFIED = 0; /** The {@link #Reason() reason} code indicating that the operation did not complete within the allowed timeout period. */ public static final int TIMEOUT = 1; /** The {@link #Reason() reason} code indicating that the identity authentication during the initial handshake with the Stage_Manager was not successful. */ public static final int UNAUTHENTICATED = 2; /** The {@link #Reason() reason} code indicating that a protocol Message contained invalid content. */ public static final int INVALID_MESSAGE = 3; private int Reason = UNSPECIFIED; /*============================================================================== Constructors */ /** Construct a Theater_Protocol_Exception with a message, reason and cause.

@param message A message String providing a context dependent description for the exception. May be null. @param reason An integer indicating the category of the reason for the exception. @param cause A Throwable that caused this Theater_Protocol_Exception to be thrown.May be null. @see Throwable#getMessage() @see #Reason() @see Throwable#getCause() */ public Theater_Protocol_Exception ( String message, int reason, Throwable cause ) { super (message); initCause (cause); Reason = reason; } /** Construct a Theater_Protocol_Exception with a message and reason.

A {@link Throwable#initCause(Throwable) cause} will not be intialized.

@param message A message String providing a context dependent description for the exception. May be null. @param reason An integer indicating the category of the reason for the exception. @see Throwable#getMessage() @see #Reason() */ public Theater_Protocol_Exception ( String message, int reason ) { super (message); Reason = reason; } /** Construct a Theater_Protocol_Exception with a reason and cause.

The exception {@link Throwable#getMessage() message} will be set to the {@link #ID} of this class.

@param reason An integer indicating the category of the reason for the exception. @param cause A Throwable that caused this Theater_Protocol_Exception to be thrown.May be null. @see #Reason() @see Throwable#getCause() */ public Theater_Protocol_Exception ( int reason, Throwable cause ) {this (ID, reason, cause);} /** Construct a Theater_Protocol_Exception with a message and cause.

The {@link #Reason() reason} for the exception will be {@link #UNSPECIFIED}.

@param message A message String providing a context dependent description for the exception. May be null. @param cause A Throwable that caused this Theater_Protocol_Exception to be thrown.May be null. @see Throwable#getMessage() @see Throwable#getCause() */ public Theater_Protocol_Exception ( String message, Throwable cause ) {this (message, UNSPECIFIED, cause);} /** Construct a Theater_Protocol_Exception with a message.

The {@link #Reason() reason} for the exception will be {@link #UNSPECIFIED}.

A {@link Throwable#initCause(Throwable) cause} will not be intialized.

@param message A message String providing a context dependent description for the exception. May be null. @see Throwable#getMessage() */ public Theater_Protocol_Exception ( String message ) {this (message, UNSPECIFIED);} /** Construct a Theater_Protocol_Exception with a cause.

The exception {@link Throwable#getMessage() message} will be set to the {@link #ID} of this class.

The {@link #Reason() reason} for the exception will be {@link #UNSPECIFIED}.

@param cause A Throwable that caused this Theater_Protocol_Exception to be thrown.May be null. @see Throwable#getCause() */ public Theater_Protocol_Exception ( Throwable cause ) {this (ID, UNSPECIFIED, cause);} /** Construct an empty Theater_Protocol_Exception. */ public Theater_Protocol_Exception () {} /*============================================================================== Accessors */ /** Get the Reason code for the exception.

The common reason codes known to Theater_Protocol_Exception are:

{@link #UNSPECIFIED}
The reason for the exception has not been specified.
{@link #TIMEOUT}
The communication channel connection operation, or the receipt of an expected Message, did not complete within the allowed timeout period.
{@link #UNAUTHENTICATED}
The identity authentication during the initial handshake with the Stage_Manager was not successful.
{@link #INVALID_MESSAGE}
A protocol Message contained invalid content.

N.B.: Other reason codes may be used, and the common reason codes may be used for other than the expected meaning. The descriptions listed above are the uses that are known.

@return An integer indicating the category of the reason for the exception. */ public int Reason () {return Reason;} } pirl-2.3.8/PIRL/Conductor/Maestro/Theater_List.java0000644000175000017500000001001411742733134021717 0ustar mathieumathieu/* Theater_List PIRL CVS ID: Theater_List.java,v 1.8 2012/04/16 06:04:12 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Conductor.Colors; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.JLabel; import javax.swing.BorderFactory; import java.awt.Component; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.Toolkit; /** A Theater_List is a list view of a Theater_List_Model.

@author Bradford Castalia, UA/PIRL @version 1.8 */ public class Theater_List extends JComboBox { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Maestro.Theater_List (1.8 2012/04/16 06:04:12)"; private Font Emphasis_Font; private static final Toolkit TOOLKIT = Toolkit.getDefaultToolkit (); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_RENDERER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Theater_List ( Theater_List_Model list_model ) { super (list_model); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Theater_List"); setEditable (true); Emphasis_Font = getFont ().deriveFont (Font.BOLD); setRenderer (new Theater_List_Renderer ()); getEditor ().addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Editor_Action ();}}); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Theater_List"); } private void Editor_Action () { String location = (String)getEditor ().getItem (); if (location == null || location.length () == 0) { getEditor ().setItem (((Theater_List_Model)getModel ()).getSelectedItem ()); TOOLKIT.beep (); } } /*============================================================================== Theater_List_Renderer */ private class Theater_List_Renderer extends JLabel implements ListCellRenderer { public Theater_List_Renderer () { setOpaque (true); setBorder (BorderFactory.createEmptyBorder (3, 0, 3, 0)); } public Component getListCellRendererComponent ( JList list, Object value, int index, boolean selected, boolean focused ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (">>> Theater_List_Renderer:" + NL +" value - " + value + NL +" index - " + index + NL +" selected - " + selected); Color color = Colors.TABLE; if (index >=0) { if (((Theater_List_Model)list.getModel ()).Opened (index)) color = Colors.OPENED_THEATER; else color = Colors.CLOSED_THEATER; } Font font = list.getFont (); if (selected) { color = Colors.Selected_Color (selected, color); font = Emphasis_Font; } setFont (font); setBackground (color); setValue (value); return this; } public void setValue ( Object value ) {setText ((value == null) ? "" : value.toString ());} } // Theater_List_Renderer } pirl-2.3.8/PIRL/Conductor/Maestro/New_Conductor_Dialog.java0000644000175000017500000005357011742733133023375 0ustar mathieumathieu/* New_Conductor_Dialog PIRL CVS ID: New_Conductor_Dialog.java,v 1.19 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Maestro; import PIRL.Messenger.Message; import PIRL.PVL.PVL_Exception; import PIRL.Viewers.Dialog_Box; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.JCheckBox; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.Box; import java.awt.BorderLayout; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Dimension; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.Frame; /** New_Conductor_Dialog provides a dialog used to interactively obtain the information needed to form a command line that will start a Conductor.

New_Conductor_Dialog is a reusable dialog. Once constructed it only needs to be setVisible to be activated. If it had been used previously the previous field values will remain.

@author Bradford Castalia, UA/PIRL @version 1.19 */ public class New_Conductor_Dialog extends JDialog { /** Class name and version identification. */ public static final String ID = "PIRL.Conductor.Maestro.New_Conductor_Dialog (1.19 2012/04/16 06:04:11)"; public static final boolean DEFAULT_WAIT_TO_START = true; private static boolean Default_Wait_to_Start = DEFAULT_WAIT_TO_START; private static final String PIPELINE = Conductor_Definition.PIPELINE_PARAMETER_NAME, CATALOG = Conductor_Definition.CATALOG_PARAMETER_NAME, CONFIGURATION = Conductor_Definition.CONFIGURATION_PARAMETER_NAME, DEFAULT_CONFIGURATION = Conductor_Definition.DEFAULT_CONFIGURATION_FILENAME, SERVER = Conductor_Definition.SERVER_PARAMETER_NAME, COUNT = Conductor_Definition.COUNT_PARAMETER_NAME, WAIT_TO_START = Conductor_Definition.WAIT_TO_START_PARAMETER_NAME; private JTextField Name_Field = new JTextField (), Pipeline_Field = new JTextField (), Catalog_Field = new JTextField (), Configuration_Field = new JTextField (), Server_Field = new JTextField (), Count_Field = new JTextField (); private String Previous_Pipeline_Name = null; private JCheckBox Wait_to_Start_Selection = new JCheckBox (); private Message Start_Conductor_Message = null; // System new-line. private static final String NL = Stage_Manager.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_GET = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_UI = 1 << 3, DEBUG_HELPERS = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a New_Conductor_Dialog.

@param title The title String for the dialog window. If null "Start Conductor" will be used. @param owner The Frame with which the dialog is associated, which may be null in which case the dialog will be associated with the display. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. @param name The name of the Conductor definition. If null the {@link #Pipeline() pipeline} name is used. @param pipeline The name of the pipeline to be managed by the Conductor. May be null, but if the corresponding field is not filled in when the Accept button is pressed a Notice Dialog_Box will be shown and the Accept will be aborted. @param catalog The name of the database catalog containing the pipeline tables. May be null. @param configuration The pathname for the configuration file to be used by Conductor. If null, the {@link PIRL.Conductor.Conductor#DEFAULT_CONFIGURATION_FILENAME} will be used. @param server The name of the database server access parameters group in the configuration file. May be null. @param count The number of Conductors to start. @param wait_to_start The {@link #WAIT_TO_START} mode for a Conductor will be set to this value. */ public New_Conductor_Dialog ( String title, Frame owner, boolean modal, String name, String pipeline, String catalog, String configuration, String server, int count, boolean wait_to_start ) { super (owner, (title == null ? "New Conductor" : title), modal); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> New_Conductor_Dialog"); if (configuration == null) configuration = DEFAULT_CONFIGURATION; Name (name); Pipeline (pipeline); Catalog (catalog); Configuration (configuration); Server (server); Count (count); Wait_to_Start (wait_to_start); // Build the panels. getContentPane ().add (Panels ()); pack (); getRootPane ().setMinimumSize (getContentPane ().getSize ()); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< New_Conductor_Dialog"); } /** Constructs a New_Conductor_Dialog.

The default name, catalog, configuration pathname, server name and count will be used and wait-to-start will be true.

@param title The title String for the dialog window. If null "Start Conductor" will be used. @param owner The Frame with which the dialog is associated, which may be null in which case the dialog will be associated with the display. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. @param pipeline The name of the pipeline to be managed by the Conductor. May be null, but if the corresponding field is not filled in when the Accept button is pressed a Notice Dialog_Box will be shown and the Accept will be aborted. */ public New_Conductor_Dialog ( String title, Frame owner, boolean modal, String pipeline ) {this (title, owner, modal, pipeline, pipeline, null, null, null, 1, Default_Wait_to_Start);} /** Constructs a New_Conductor_Dialog.

The default name, catalog, configuration pathname, pipeline name, server name and count will be used and wait-to-start will be true.

@param title The title String for the dialog window. If null "Start Conductor" will be used. @param owner The Frame with which the dialog is associated, which may be null in which case the dialog will be associated with the display. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public New_Conductor_Dialog ( String title, Frame owner, boolean modal ) {this (title, owner, modal, null, null, null, null, null, 1, Default_Wait_to_Start);} /** Constructs a New_Conductor_Dialog.

The default name, catalog, configuration pathname, pipeline name, server name and count will be used and wait-to-start will be true. The dialog will be modal.

@param title The title String for the dialog window. If null "Start Conductor" will be used. @param owner The Frame with which the dialog is associated, which may be null in which case the dialog will be associated with the display. */ public New_Conductor_Dialog ( String title, Frame owner ) {this (title, owner, true, null, null, null, null, null, 1, Default_Wait_to_Start);} /** Constructs a default New_Conductor_Dialog. */ public New_Conductor_Dialog () {this (null, null, false, null, null, null, null, null, 1, Default_Wait_to_Start);} /** Get a Message that defines a command line to start a Conductor.

A New_Conductor_Dialog is constructed, set visible and the resulting {@link #Start_Conductor_Message() message}, if any, is returned. The dialog will be modal and the default pipeline name, catalog, configuration pathname, server name and count will be used and wait-to-start will be true.

@param title The title String for the dialog window. If null "Start Conductor" will be used. @param owner The Frame with which the dialog is associated, which may be null in which case the dialog will be associated with the display. */ public static Message Start_Conductor_Message ( String title, JFrame owner ) { New_Conductor_Dialog dialog = new New_Conductor_Dialog (title, owner, true, null, null, null, null, null, 1, Default_Wait_to_Start); dialog.setVisible (true); return dialog.Start_Conductor_Message (); } /*============================================================================== Accessors */ /** Get the dialog title.

@return The title String to be displayed in the dialog window header bar. @see #Title(String) */ public String Title () {return getTitle ();} /** Set the dialog title.

@param title The title String to be displayed in the dialog window header bar. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Title ( String title ) {setTitle (title); return this;} /** Get the Conductor name.

@return The name of the Conductor definition. This will be null if no name has been specified. @see #Name(String) */ public String Name () { String name = Name_Field.getText (); if (name.length () == 0) name = null; return name; } /** Set the Conductor name.

@param name The name of the Conductor definition. If null the {@link #Pipeline() pipeline} name is used. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Name ( String name ) { if (name == null) name = Pipeline (); Name_Field.setText (name); return this; } /** Get the pipeline name.

@return The name of the pipeline. This will be null if no pipeline has been specified. @see #Pipeline(String) */ public String Pipeline () { String name = Pipeline_Field.getText (); if (name.length () == 0) name = null; return name; } /** Set the pipeline name.

If the {@link #Name() Conductor name} is null it is set to the pipeline name.

@param name The name of the pipeline. May be null. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Pipeline ( String name ) { Previous_Pipeline_Name = Pipeline_Field.getText (); Pipeline_Field.setText (name); if (Name () == null) Name (name); return this; } /** Get the database catalog name.

@return The name of the catalog. This will be null if no catalog has been specified. @see #Catalog(String) */ public String Catalog () { String name = Catalog_Field.getText (); if (name.length () == 0) name = null; return name; } /** Set the database catalog name.

@param name The name of the database catalog. May be null. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Catalog ( String name ) {Catalog_Field.setText (name); return this;} /** Get the configuration source name.

@return The source name for the configuration file. This will be null if no configuration source has been specified. @see #Configuration(String) */ public String Configuration () { String name = Configuration_Field.getText (); if (name.length () == 0) name = null; return name; } /** Set the configuration source name.

@param name The source name for the configuration file. May be null. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Configuration ( String name ) {Configuration_Field.setText (name); return this;} /** Get the database server name.

@return The name of the database server as listed in the Server list of the configuration file used by the Conductor. This will be null if no database server has been specified. @see #Server(String) */ public String Server () { String name = Server_Field.getText (); if (name.length () == 0) name = null; return name; } /** Set the database server name.

@param name The name of the database server as listed in the Server list of the configuration file used by the Conductor. May be null. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Server ( String name ) {Server_Field.setText (name); return this;} /** Test if wait-to-start is selected.

@return true if wait-to-start has been selected; false otherwise. @see #Wait_to_Start(boolean) */ public boolean Wait_to_Start () { return Wait_to_Start_Selection.isSelected (); } /** Set the wait-to-start mode.

When wait-to-start mode is enabled the Conductor will be told to wait for a start message from a Manager before source processing begins. Otherwise the Conductor will begin processing sources immediately after it has initialized.

@param wait_to_start The value of the Coductor wait-to-start mode. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Wait_to_Start ( boolean wait_to_start ) {Wait_to_Start_Selection.setSelected (wait_to_start); return this;} /** Set the default wait-to-start mode.

@return The default value of the Coductor wait-to-start mode. @see #Default_Wait_to_Start(boolean) */ public static boolean Default_Wait_to_Start () {return Default_Wait_to_Start;} /** Get the default wait-to-start mode.

@param wait_to_start The default value of the Coductor wait-to-start mode if it is not otherwise specified. @see #Wait_to_Start(boolean) */ public static void Default_Wait_to_Start ( boolean wait_to_start ) {Default_Wait_to_Start = wait_to_start;} /** Get the Conductor count.

@return The number of Conductors to be started. @see #Count(int) */ public int Count () {return Integer.parseInt (Count_Field.getText ());} /** Set the Conductor count.

@param count The number of Conductors to be started. The value will be limited to the range 1 - {@link Stage_Manager#DEFAULT_MAX_START_CONDUCTORS_COUNT}. N.B.: The Stage_Manager being employed may have been configured to impose a lower maximum value. @return This New_Conductor_Dialog. */ public New_Conductor_Dialog Count ( int count ) { if (count <= 0) count = 1; else if (count > Stage_Manager.DEFAULT_MAX_START_CONDUCTORS_COUNT) count = Stage_Manager.DEFAULT_MAX_START_CONDUCTORS_COUNT; Count_Field.setText (String.valueOf (count)); return this; } /** Get the Conductor command line description message.

@return The Message that was generated the last time this dialog was used and Accept was pressed. This will be null if the dialog was not previously used or the last time it was used Cancel was pressed. */ public Message Start_Conductor_Message () {return Start_Conductor_Message;} /*============================================================================== Panels */ private JPanel Panels () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> New_Conductor_Dialog.Panels"); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Text fields: // Name. location.anchor = GridBagConstraints.EAST; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (5, 5, 5, 3); panel.add (new JLabel ("Conductor name:"), location); Name_Field.setToolTipText ("A name for this Conductor definition
" +"Default: The pipeline name"); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (5, 0, 5, 5); panel.add (Name_Field, location); // Pipeline. location.anchor = GridBagConstraints.EAST; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (5, 5, 5, 3); panel.add (new JLabel ("Pipeline name:"), location); Pipeline_Field.setToolTipText ("Pipeline for Conductor to manage"); Dimension dimension = Pipeline_Field.getPreferredSize (); dimension.width = 175; Pipeline_Field.setMinimumSize (dimension); Pipeline_Field.setPreferredSize (dimension); Pipeline_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { if (Pipeline () != null && (Name () == null || Name ().equals (Previous_Pipeline_Name))) Name (Pipeline ()); }}); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (5, 0, 5, 5); panel.add (Pipeline_Field, location); // Configuration. location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 5, 5, 3); panel.add (new JLabel ("Configuration file:"), location); Configuration_Field.setToolTipText ("Conductor configuration file pathname
" + NL +"Default: " + DEFAULT_CONFIGURATION); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 5); panel.add (Configuration_Field, location); // Server. location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 5, 5, 3); panel.add (new JLabel ("Database Server:"), location); Server_Field.setToolTipText ("Server name in the configuration file
" + NL +"Default: First Server name listed"); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 5); panel.add (Server_Field, location); // Catalog. location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 5, 5, 3); panel.add (new JLabel ("Database catalog:"), location); Catalog_Field.setToolTipText ("Database catalog containing the pipeline tables
" + NL +"Default: Catalog in the configuratin file"); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 5); panel.add (Catalog_Field, location); // Count. location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 5, 5, 3); panel.add (new JLabel ("Conductor count:"), location); Count_Field.setToolTipText ("Number of Conductors to start
" + NL +"Default: 1"); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 5); panel.add (Count_Field, location); // Wait-to-Start. location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 5, 5, 3); panel.add (new JLabel ("Wait to start:"), location); Wait_to_Start_Selection.setToolTipText ("Wait for a Manager to start the Conductor"); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 5); panel.add (Wait_to_Start_Selection, location); // Buttons panel: JPanel button_panel = new JPanel (new GridBagLayout ()); JButton button; location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 0, 0); // Accept. button = new JButton ("OK"); button.setMnemonic ('O'); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Completed (true);}}); button.setDefaultCapable (true); getRootPane ().setDefaultButton (button); location.anchor = GridBagConstraints.WEST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); // Cancel. button = new JButton ("Cancel"); button.setMnemonic ('C'); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Completed (false);}}); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.EAST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 5, 5, 5); panel.add (button_panel, location); if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< New_Conductor_Dialog.Panels"); return panel; } /*============================================================================== Actions */ /** Completes the dialog interaction. */ private void Completed ( boolean accept ) { Start_Conductor_Message = null; if (accept) { String name = Pipeline (); if (name == null) { Dialog_Box.Notice ("A pipeline name must be specified.", this); return; } int count = 1; try {count = Count ();} catch (NumberFormatException exception) { Dialog_Box.Notice ("The Conductor count must be a number."); return; } if (count < 1 || count > Stage_Manager.DEFAULT_MAX_START_CONDUCTORS_COUNT) { Dialog_Box.Notice ("The Conductor count must be in the range 1 - " + Stage_Manager.DEFAULT_MAX_START_CONDUCTORS_COUNT + '.'); return; } if (Start_Conductor_Message == null) Start_Conductor_Message = Message.Action (Stage_Manager.START_CONDUCTORS_ACTION); Start_Conductor_Message.Name (Name ()); Start_Conductor_Message .Set (PIPELINE, name) .Set (CATALOG, Catalog ()) .Set (CONFIGURATION, Configuration ()) .Set (SERVER, Server ()) .Set (COUNT, count); if (Wait_to_Start ()) Start_Conductor_Message .Set_Token (WAIT_TO_START); } setVisible (false); } /* // Test stub. public static void main ( String[] args ) { Message message = Start_Conductor_Message ("Start Conductor", null); if (message != null) System.out.println (message); System.exit (0); } */ } // End of New_Conductor_Dialog class. pirl-2.3.8/PIRL/Conductor/Pipeline_Source_Manager0000755000175000017500000001565711076306635021544 0ustar mathieumathieu#!/bin/csh -f # # Pipeline_Source_Manager # # A wrapper for the Java Conductor pipeline sources manager. # # Environment variables used and their default values: # # PIRL_JAVA_HOME - /opt/java # This may be a directory pathname where the PIRL subdirectory and # all its class files is located; or it may be the pathname to the # PIRL.jar file containing all dependencies. On Darwin systems the # ~/Library/Java/Extensions and /Library/Java/Extensions directory # will be checked for the PIRL.jar file. # # The following are used only if PIRL_JAVA_HOME is not a jar file. # # MySQL_JDBC - $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # The pathname to the MySQL JDBC driver jar file or the root # directory where its class files are located. # (http://dev.mysql.com/downloads/connector/j/) # # PostgreSQL_JDBC - $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. # (http://jdbc.postgresql.org/) # # JCM - $PIRL_JAVA_HOME/jcm/jcm_data.jar # The pathname to the Java Components for Mathematics jar file or # the root directory where its class files are located. # (http://math.hws.edu/javamath) # # SwingX - $PIRL_JAVA_HOME/SwingX/swingx.jar # The pathname to the SwingLabs Swing Component Extensions jar # file or the root directory where its class files are located. # (http://swinglabs.org/) # # CVS ID: Pipeline_Source_Manager,v 1.2 2008/10/18 07:17:49 castalia Exp set PIRL_root = /opt/java set Darwin_Extensions = Library/Java/Extensions set MySQL_JDBC_dir = mysql-connector set MySQL_JDBC_jar = mysql-connector.jar set PostgreSQL_JDBC_dir = PostgreSQL set PostgreSQL_JDBC_jar = postgresql.jar set JCM_dir = jcm set JCM_jar = jcm_data.jar set SwingX_dir = SwingX set SwingX_jar = swingx.jar set OS = `uname -s` # Location of the PIRL Java Packages. if (! $?PIRL_JAVA_HOME) then if (-e $PIRL_root/PIRL) then set PIRL_JAVA_HOME = $PIRL_root else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = ~/$Darwin_Extensions/PIRL.jar else if (-e /$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = /$Darwin_Extensions/PIRL.jar endif endif if (! $?PIRL_JAVA_HOME) then echo "Set the PIRL_JAVA_HOME environment variable" echo "to the location of the PIRL Java Packages." exit 1 endif endif if (! -e $PIRL_JAVA_HOME) then echo "No such file or directory: $PIRL_JAVA_HOME" echo "The PIRL Java Packages are required." exit -1 endif set classpath = $PIRL_JAVA_HOME if ($PIRL_JAVA_HOME !~ *.jar) then # Database drivers: # Location of the MySQL JDBC driver. if (! $?MySQL_JDBC) then if (-e $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar) then set MySQL_JDBC = $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = ~/$Darwin_Extensions/$MySQL_JDBC_jar else if (-e /$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = /$Darwin_Extensions/$MySQL_JDBC_jar endif endif if ($?MySQL_JDBC) then set classpath = ${classpath}:$MySQL_JDBC endif else if (! -e $MySQL_JDBC) then echo "No such file or directory: $MySQL_JDBC" echo "MySQL database support is not available." unset MySQL_JDBC unsetenv MySQL_JDBC else set classpath = ${classpath}:$MySQL_JDBC endif endif # Location of the PostgreSQL JDBC driver. if (! $?PostgreSQL_JDBC) then if (-e $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar else if (-e /$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = /$Darwin_Extensions/$PostgreSQL_JDBC_jar endif endif if ($?PostgreSQL_JDBC) then set classpath = ${classpath}:$PostgreSQL_JDBC endif else if (! -e $PostgreSQL_JDBC) then echo "No such file or directory: $PostgreSQL_JDBC" echo "PostgreSQL database support is not available." unset PostgreSQL_JDBC unsetenv PostgreSQL_JDBC else set classpath = ${classpath}:$PostgreSQL_JDBC endif endif if (! $?MySQL_JDBC && ! $?PostgreSQL_JDBC) then echo "Database support is required." echo echo "One or both of the following will provide database support:" echo echo "Use the MySQL_JDBC environment variable" echo "to set the location of the $MySQL_JDBC_jar file" echo "or place it in the $PIRL_root/$MySQL_JDBC_dir directory." echo "See http://dev.mysql.com/downloads/connector/j/" echo echo "Use the PostgreSQL_JDBC environment variable" echo "to set the location of the $PostgreSQL_JDBC_jar file" echo "or place it in the $PIRL_root/$PostgreSQL_JDBC_dir directory." echo "See http://jdbc.postgresql.org/" Jar_Location_Note: if ($OS == "Darwin") then echo echo "Jar files may also be placed in the" echo "~/$Darwin_Extensions or" echo "/$Darwin_Extensions directories." endif echo exit 1 endif # External Java packages: # Location of the Java Components for Mathematics. if (! $?JCM) then if (-e $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar) then set JCM = $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$JCM_jar) then set JCM = ~/$Darwin_Extensions/$JCM_jar else if (-e /$Darwin_Extensions/$JCM_jar) then set JCM = /$Darwin_Extensions/$JCM_jar endif endif if (! $?JCM) then goto JCM_Note endif else if (! -e $JCM) then echo "No such file or directory: $JCM" JCM_Note: echo echo "The Java Components for Mathematics are required." echo "Use the JCM environment variable" echo "to set the location of the $JCM_jar file" echo "or place it in the $PIRL_root/$JCM_dir directory." echo "See http://math.hws.edu/javamath" goto Jar_Location_Note endif endif set classpath = ${classpath}:$JCM # Location of the SwingLabs Swing Component Extensions. if (! $?SwingX) then if (-e $PIRL_JAVA_HOME/$SwingX_dir/$SwingX_jar) then set SwingX = $PIRL_JAVA_HOME/$SwingX_dir/$SwingX_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$SwingX_jar) then set JCM = ~/$Darwin_Extensions/sw$SwingX_jar else if (-e /$Darwin_Extensions/swi$SwingX_jar) then set JCM = /$Darwin_Extensions/$SwingX_jar endif endif if (! $?SwingX) then goto SwingX_Note endif else if (! -e ${SwingX}) then echo "No such file or directory: ${SwingX}" SwingX_Note: echo echo "The SwingLabs Swing Component Extensions are required." echo "Use the SwingX environment variable" echo "to set the location of the $SwingX_jar file" echo "or place it in the $PIRL_root/$SwingX_dir directory." echo "See http://swinglabs.org/" goto Jar_Location_Note endif endif set classpath = ${classpath}:${SwingX} endif exec java -cp $classpath PIRL.Conductor.Pipeline_Source_Manager $argv:q pirl-2.3.8/PIRL/Conductor/Conductor.java0000644000175000017500000063602711742733131017675 0ustar mathieumathieu/* Conductor PIRL CVS ID: Conductor.java,v 2.47 2012/04/16 06:04:09 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; // PIRL packages import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Database.Database; import PIRL.Database.Fields_Map; import PIRL.Database.Database_Exception; import PIRL.PVL.Parameter; import PIRL.PVL.PVL_Exception; import PIRL.Utilities.Host; import PIRL.Utilities.UNIX_Process; import PIRL.Utilities.Styled_Multiwriter; import PIRL.Utilities.Styled_Writer; import PIRL.Utilities.Multiwriter_IOException; import PIRL.Strings.String_Buffer; import PIRL.Conductor.Maestro.Local_Theater; import PIRL.Conductor.Maestro.Theater_Protocol_Exception; import PIRL.Messenger.Message; // JCM package for expression parser/evaluator. import edu.hws.jcm.data.Parser; import edu.hws.jcm.data.ParseError; import java.util.Collections; import java.util.Vector; import java.util.Date; import java.util.StringTokenizer; import java.io.File; import java.io.Writer; import java.io.FileWriter; import java.io.PrintWriter; import java.io.StringWriter; import java.io.PrintStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.ConnectException; import java.text.ParseException; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import java.lang.reflect.Method; /** Conductor is a queue management mechanism for the sequential processing of data files.

A Conductor processes a list of data source files through a list of procedures to be invoked on each file. These lists are obtained from a Database as a pair of tables. The list of files is contained in a Sources table and the procedures are defined in a Procedures table. A pair of Sources and Procedures tables constitutes a Pipeline: each Sources record is processed in the order it occurs in the table (FIFO); each procedure specified by a Procedures record is executed in sequence number order. Each pipeline has a name that is used to find its database tables, where the pipeline tables are named:

<pipeline>_Sources
<pipeline>_Procedures

A Conductor must be run with a {@link #Usage command line} argument that specifies the pipeline it is to process. Multiple Conductors may safely process the same pipeline from the same or separate host systems.

Database

Conductor requires a database containing the pipeline tables to be accessible. It is accessed using the PIRL {@link Database} package. This package abstracts the particulars of database access. The Conductor configuration file specifies the database access information.

Sources Table

A Sources table must contain at least these fields:

Source_Number
Must be a non-NULL integer value unique for each record. While the order does not matter, it is easiest to make this a field with a value that is automatically assigned and incremented by the database server.
Source_ID
A string that identifies the file in some user specific manner. It is used, along with the Source_Number, to produce what is expected to be a unique log filename. If the field value is NULL or empty then the filename portion of the Source_Pathname, with any extension removed, will be used and this field will be updated.
Source_Pathname
This is the pathname of the file that is to be processed. The pathname is in the syntax of the host system. It is not required that the pathname be fully qualified, only that the file can be found using the pathname. This field value must not be NULL or empty.
Conductor_ID
This is a text field that must be NULL (not just empty) for each record to be processed. It is filled in with the name of the Conductor host system, possibly supplemented by the process ID of the Conductor (see the System Dependencies, Conductor_ID section, below), when the record is acquired for processing. This field provides an exclusive lock against other Conductor processes acquiring the record: Once a Conductor sets this field the record is no longer available for processing.
Status
An indicator of the status of each procedure invoked on the source file is recorded in this field. As each procedure is invoked on the source file this field is kept current with the status of the procedure. The format of the field value is described for the {@link #Status_Indicators(String) Status_Indicators} method which, along with other Status_xxx methods, provides a convenient means for other Java classes to interpret and manipulate these field values. Note: This field should initially be NULL or empty. It is important that this field only be modified consistent with the operation of Conductor (i.e. change at your own risk!).
Log_Pathname
The pathname for the log file where the processing of the source file is recorded. Normally this field is left empty (or NULL) and the Log_Pathname (or Log_Directory) parameter of the Configuration file will be used. If either of these is a pathname that refers to an existing directory, then Conductor will generate an appropriate log filename (see the description of the Log_Directory and Log_Filename parameters, below). If this field is empty and both parameters are empty or not present, the log file will be written to the current working directory (the Log_Directory parameter is empty) However, if either this field or the Log_Pathname (or Log_Directory) parameter is not empty and does not refer to an existing directory then the pathname is to a regular file then that file will be appended with the source file log (a new file will be created if it does not yet exist). Both this field value and any parameter used will be reference resolved if not empty. N.B.: If a pathname to an existing log file pathname is not specified by this field or a configuration parameter then any existing file at the generated pathname is overwritten. This field is always updated by whatever actual log file pathname is used.

A Sources table may contain the required fields in any order, and it may contain additional fields as desired. For example, it is recommended that a timestamp field be provided that will automatically be updated with the last update time of each record.

Procedures Table

A Procedures table must contain at least these fields:

Sequence
A real number value that orders the procedure in the sequence of processing the source file. The values need not be sequential nor must they be in any particular order in the table. All of the procedure records will be sorted numerically on this field value so that processing of the source file will occur in sequence order. It is strongly recommended that the values be unique in the table, but this is not required; however there is no certainty of the order of processing for procedures with the same sequence number.
Command_Line
The command line to be submitted to the system for executing the procedure. The command line may contain embedded field and/or parameter {@link Reference_Resolver references} to be substituted with database field values and/or configuration parameter values. Obviously this field must be neither empty nor NULL.
Success_Status
An integer value that matches the exits status of the procedure when the procedure has completed successfully. The value can be text that is subject to embedded reference resolving, but must ultimately be convertible to an integer value. If this field is NULL or empty and the Success_Message field is not, then the latter field is used instead. If both fields are empty then the Empty_Success_Any parameter control how this will be interpreted. This field may contain text that is reference resolved but must produce an integer value.
Success_Message
Text to match on the procedure output lines - either stdout or stderr - to determine if the procedure completed successfully. This field is only used if the Success_Status field is empty or NULL. The text in this field is reference resolved. The {@linkplain java.lang.String#matches(String) match} against the lines of procedure output treats the resolved text as a regular expression {@link java.util.regex.Pattern pattern}.
Time_Limit
The maximum amount of time, in seconds, to wait for the procedure to complete. The value can be text that is subject to embedded reference resolving. After resolving any references the result is treated as a mathematical expression that must produce a single integer value. A 0 (zero) or negative value indicates an unlimited wait time. An empty or NULL value is equivalent to 0. If the procedure does not complete within the specified amount of time it is killed.
On_Failure
A command line just like the Command_Line field. If the procedure defined by the Command_Line field fails to complete successfully, then the procedure defined by the On_Failure field is run. No time limit is applied to this procedure.

A Procedures table may contain the required fields in any order, and it may contain additional fields as desired. If a "Description" field is present, which is higly recommended, it is used as a text description of each procedure included in the processing log. It is also recommended that a timestamp field be provided that will automatically be updated with the last update time of each record.

Database Connection Resilience

A Conductor maintains a connection with the {@link Database} server while it is operating. If any access to the Database by Conductor fails due to a loss of the connection, Conductor will attempt to reconnect and, if successful, repeat the access operation again (a connection failure of the repeated access operation does not result in a reconnection attempt). If the reconnection fails because the connection can not be established with the server, the attempt will be retried after a delay period of 5 minutes (this can be overridden with the {@link #RECONNECT_DELAY_PARAMETER}). Up to 16 retries (this can be overridden with the {@link #RECONNECT_TRIES_PARAMETER}) will be attempted before a database access failure is deemed to have occurred. N.B.: Database connection resilience does not automatically apply to any database access operations by pipeline procedures.

Configuration

When a Conductor is started it first reads its {@link Configuration Configuration} file. This is "Conductor.conf" by default, but another filename may be specified on the {@link #Usage command line}. The configuration file contains parameter definitions in Parameter Value Language ({@linkplain PIRL.PVL.Parser PVL}) format. This file contains the information needed to access the database used by Conductor as well as any other parameters that may be useful to {@linkplain Reference_Resolver resolve} parameter references embedded in procedure definition record field values. Since references may contain nested references it is quite appropriate for users to provide configuration parameters with values that are database field references (perhaps with complex conditionals and multiple field combinations) so that Command_Line (for example) definitions use the user specified parameter references rather than the more complicated definitions. This also makes it easy to modify the field reference definitions, just by editing the configuration file, without necessarily needing to change the contents of a Procedures pipeline table.

N.B.: By default, when the configuration file is read during startup by the application's main method should any parameters have the same pathname the last duplicate encountered is given preference. This is especially important to keep in mind when the configuration file {@link Configuration#Include() includes} another configuration file, such as a site-wide configuration. For example, a site-wide configuration file included in all pipeline-specific configuration files (a typical scenario) might have a Conductor group that includes default parameters such as Stop_on_Failure with a small value (e.g. 1 or 2) to prevent a bug in a pipeline procedure from generating a large number of source processing failures, while a configuration file for a specific pipeline that is expected to have failures (which may actually be branches off to some other pipeline depending on the outcome of some condition testing procedure) might have a Conductor group that includes a Stop_on_Failure with a large (or possibly zero) value. As long as the site-wide configuration file is included before the pipeline-specific Conductor/Stop_on_Failure parameter is specified the latter will take precedence over the former.

Parameters

The Conductor automatically provides a set of parameters in the configuration Conductor group:

Class_ID
The fully qualified Conductor class name with its revision number and date.
Configuration_Source
The name of the configuration file source. This may be the name of a file on the Conductor host system or a URL for a file obtained remotely.
Conductor_ID
The Conductor identification (see the System Dependencies, Conductor_ID section, below).
Database_Server_Name
The name of the database Server configuration parameters group. If no Server name could be determined, this parameter will not be included.
Hostname
The fully qualified hostname of the system where Conductor is running. If the hostname can not be obtained the IP (internet protocol) address will be used.
Database_Hostname
The hostname of the database server system.
Database_Type
The type of database server, as known to the {@link Database} access package.
Pipeline
The simple pipeline name (without any catalog prefix).
Catalog
The name of the database catalog where the pipeline tables are located.
Sources_Table
The full name of the table, including the Catalog prefix, containing the Source file records.
Procedures_Table
The full name of the table containing the procedure definitions.

Dynamic source parameters

The following parameters are reset for each source record being processed:

Log_Directory
The pathname to the directory where the log file is written. If a Log_Pathname field value is present it is used to determine the Log_Directory, but only for the current source record. Otherwise the Log_Pathname configuration parameter is used. If it is not present or empty the Log_Directory parameter is used instead. The default Log_Directory is Conductor's current working directory.
Log_Filename
The filename (without the directory path) of the source log file. If the neither the source record Log_Pathname field nor the Log_Pathname or Log_Directory parameters has a non-empty value that does not refer to an existing directory - a value that does not refer to an existing directory is taken to be the pathname to the log file - then a default filename will be generated that has the form:
<Pipeline>-<Source_ID>_<Source_Number>.log

The Pipeline name includes the leading database catalog name separated by a period ('.') character.

The Source_ID and Source_Number are obtained from the current source record. Note, however, that there is a chance that the Source_ID will include characters that are unsafe for use as part of a filename. Assuming that the only unsafe character is the system property "file.separator" character ('/' for Unix), it will be replaced with a percent ('%') character.

Note: If either the source record field or configuration parameter value is not empty and does not refer to a an existing directory, then that value will be used unconditionally to determine the log filename.

Source_Number
The Source_Number field value of the current source record.
Source_ID
The Source_ID field value of the current source record.
Source_Pathname
The Source_Pathname field value of the current source record in the filesystem's fully qualified (absolute) form.
Source_Directory
The directory path portion of the Source_Pathname value.
Source_Filename
The filename portion (without the directory pathname) of the Source_Pathname value.
Source_Filename_Extension
The portion of the Source_Filename value following the last period ('.') character in the name. This will be the empty string if there is no extension.
Source_Filename_Root
The portion of the Source_Filename value without the extension (the portion preceding the last period character). This may be the empty string.

Dynamic procedure parameters

The following parameters are reset for each procedure record being processed:

Total_Procedure_Records
The total number of procedure definition records in the procedures table. Note: This a a dynamic parameter because the procedures table is refreshed each time pipeline processing is started and the table may have been changed while the Conductor was waiting.
Procedure_Count
The procedure definition record count for the current, or last, procedure sequence. The first procedure definition record has a count of one. A Procedure_Count of zero means that processing for the current source record has not yet commenced.
Sequence
The Sequence field value of the current, or last. procedure record.
Completion_Number
The completion number for the last procedure that was executed. If the procedure ran to completion, whether it was successful or not, it will be the exit status value (a non-negative integer value) of the procedure. If the procedure did not complete for any reason it will be a negative Conductor completion code; the {@link #Status_Conductor_Code_Description(int) Status_Conductor_Code_Description} static method may be used to obtain a brief, one line description of this code.

Conductor control parameters

The following configuration parameters will be used if they are present in the Conductor group or parent of this group:

Unresolved_Reference
The value to use for an unresolved reference. By default an unresolved referenced throws a Database_Exception. This can be specified with a value beginning with the word "throw" (case insensitive). Note: All parameters used by Conductor are reference resolved. Those with unresolved references that would throw an exception are deemed to be missing parameters. Those values that have incorrect reference syntax are left unresolved.
Empty_Success_Any
If "true", when Success_Status and Success_Message are both empty or NULL the corresponding procedure is always deemed successful when it completes. Otherwise this condition implies a zero (0) Success_Status. Default: false.
Min_Source_Records
The minimum number of source records to be processed in batch mode before Conductor stops. Default: 1.
Max_Source_Records
The maximum number of source records to obtain at any one time from the database. This prevents memory exhaustion if the number of unprocessed source records is very large. Must not be less than Min_Source_Records. Default: 1000.
Poll_Interval
The amount of time (in seconds) to wait before trying to obtain more unprocessed source records when querying the source table found no unprocessed records. If this value is zero or negative Conductor processing will stop instead of waiting to try again. Default: 30.
Source_Available_Tries
The number of tries that will be made to confirm that the Source_Pathname is accessible - i.e. exists as a regular file that can be read - before giving up and declaring the file to be inaccessible. After each accessibility check failure, and before the next try, a ten second pause will be provided. The intention is to give filesystem directory caches time to be synchronized with newly created files on remote filesystems. A maximum of 180 retries (30 minutes wait time) is allowed to prevent Conductor from waiting indefinitely. If the value is negative no Source_Pathname confirmation will be done. This can be useful if the pipeline does not process a source file. Default: 12.
Reconnect_Tries
The maximum number of reconnection tries if the database connection is lost. Default: 16.
Reconnect_Delay
The delay, in seconds, between database reconnection retry attempts. Default: 300.
Stop_on_Failure
The number of sequential source processing failures that will cause Conductor to stop further processing. Zero means source failures will never cause processing to stop. "true" or "yes" is equivalent to 1; "false" or "no" is equivalent to 0. Default: 0;
Notify
A list of zero or more email address that will be sent a notification if Conductor processing halts. The reason processing halted will be in the email message.

Stage_Manager parameters

The Conductor will try to connect to a Stage_Manager process on the local host system. The Stage_Manager provides remote Management capabilities. A host system may be running multiple Stage_Maangers, each using its own communications port, to provide mutiple Theater management contexts for different sets of Conductors. A Conductor can operate in only one Theater as determined by its Stage_Manager parameters. The following configuration parameters, which must be in a Stage_Manager sub-group of the Conductor group, control the Conductor connection to the Theater's Stage_Manager:

Require_Stage_Manager
If "enabled", "true", "yes" or 1 a connection to a Stage_Manager is required for this Conductor or it will not run. N.B.: This parameter is only read once when the Conductor is first configured; when the Conductor is reconfigured on being restarted after a Stop or Halt state the initial value is reset in the internal Configuration regardless of any change to the external configuration source.
Timeout
The amount of time (seconds) to wait for a Stage_Manager connection to complete before the connection attempt fails.
Password
The password required to authenticate a Stage_Manager connection. The value is a text string of any length. If this parameter is present its value is masked out in the internal Configuration. If this parameter is not present, or has an empty value, and the Stage_Manager requires an authenticated connection the connection attempt will fail.
{@link #HELLO_PORT_PARAMETER_NAME} - Get and Set
The port number to use when listening for a Stage_Manager "Hello" broadcast that it is ready for connections.
{@link #HELLO_ADDRESS_PARAMETER_NAME} - Get and Set
The multicast address to use when listening for a Stage_Manager "Hello" broadcast that it is ready for connections.

Processing

Procedure Records

After the Conductor has been initialized using its configuration file and connected to the database it begins to process the pipeline. Note: If the Conductor is run with a Manager (using the -Manager command line option), processing does not begin immediately; the Conductor will wait until it is told to start by the Manager. The records from the Procedures table are read, confirmed that they contain all necessary fields, and sorted into Sequence number order. A Conductor may be told to stop processing by a Manager (including a remote Manager), which will cause the Conductor to stop further processing when the current source record processing is complete. Processing is resumed with the next source record when a Manager sends the start signal. Each time pipeline processing is started the Procedures is loaded again and the configuration file is read again and used to reconfigure the Conductor. Changes to the Procedures records and configuration file can safely be made while Conductor is running.

Source Records

When pipeline processing has been started Conductor begins processing records from its Sources table. All unprocessed records, up to a maximum (set by default to 1000 to prevent memory exhaustion) configurable with the Max_Source_Records parameter, are read into an internal cache. An unprocessed record has a NULL in its Conductor_ID field. These records are processed in first-in first-out (FIFO) order.

An exclusive lock must be acquired on a record before it can be processed. To acquire a lock on a source record an attempt is made to update the record's Conductor_ID field to the Conductor's identification value (hostname and possibly process ID) with the condition that the field value is currently NULL. The update operation by the database server is atomic; once the operation is started by the database server it will go to completion without the possibility of interruption by any other database operation. This guarantees that only one process will be able to gain access to any source record even in the context of multiple processes contending for the same record at the same time. If some other Conductor has already acquired the record, as indicated by the failure of the update operation (because the Conductor_ID field is no longer NULL as required), the record will be removed from the cache and the Conductor will try to acquire a lock on the next record in the cache. If the update succeeds then the Conductor has acquired exclusive control of the record. It will be safe to process the source without concern that some other process may interfere.

The first step in processing a source record is to open a log file to record the processing. If the source record Log_Pathname field is empty the user's Log_Pathname configuration parameter will be used. If that is absent or empty the Log_Directory parameter will be used. If that, too, is absent or empty a default filename will be generated - as described in the Log_Filename parameter description, above - and the log file will be written the current working directory. If either the source record field or configuration parameter is not empty the value is resolved for any embedded {@linkplain Reference_Resolver references}. If the resulting pathname refers to an existing directory the default filename is added. Otherwise the pathname is taken to refer to a file to which processing log output is to be appended (the file will be created if it does not exist). : In all other cases - whenever a default filename is generated - an existing file will be overwritten. The log file pathname that is used is always updated to the source record Log_Pathname field. Note: A source record will not be processed without a log file; it is fatal to Conductor not to have a writable log file available for each source record.

The log file always begins with the Conductor class identification:

PIRL.Conductor.Conductor (2.47 2012/04/16 06:04:09)

This line is immediately followed by a {@link #SOURCE_FILE_LOG_DELIMITER} line which is expected to be 70 equals ('=') characters. This is followed by a date and time stamp and the source record description including the database server type and hostname, the fully qualified name of the Sources Table (Sources_Table) and the Source_Number, Source_ID, and Source_Pathname values.

The Status field of the source record is checked for any status indicators from possible previous processing. If present they are logged. If the last status indicator is for a failure condition this is logged and any further processing of the source is skipped. Note: It is possible to (re)start processing of a source midway in its procedures pipeline. This is done by first setting its Status field to include status indicators for procedures to be skipped; e.g. by removing a failure indicator at the end of the list after correcting the cause of the failure. Then the Conductor_ID field is set to NULL (as long as this is done last it will be safe even if the actively being processed by a Conductor). When the source record is acquired by a Conductor its processing will begin with the next procedure without a status indicator, and log output will be appended to its previous log file if present or a new file if needed. Caution: Procedure pipelines to be used in this way must, of course, ensure that any dependencies on previous procedures are taken into account.

At this point configuration parameters dependent on the current source record are updated.

The Source_Pathname file is confirmed to be a normal file (not a directory) that is readable. If the file is not accessible the Status field of the source record is updated in the database with the {@link #INACCESSIBLE_FILE} Conductor completion code, the Completion_Number parameter is set to the same value, the condition is logged and further processing of the source record is canceled.

Procedures Pipeline

The source now enters the procedures pipeline. The procedure definition records are all cached and sorted by their Sequence number when Conductor first starts. Thus changes to a Procedures table will not take effect until after a Conductor (re)starts, and it is safe to change a Procedures table while a Conductor is running.

Each procedure record is applied to the current source record in Sequence number order; the Sequence parameter is updated before each procedure is processed.

Embedded References

All of the required Procedure fields, except the Sequence number, may contain embedded {@linkplain Reference_Resolver references}. Each embedded reference is effectively a variable that is substituted with the value from a database field or configuration parameter specified by the reference. References may be arbitrarily nested; for example, the condition for selecting a record in a database field reference may be a parameter reference supplied in a configuration parameter. Reference resolution is also recursive; the value obtained from resolving a reference may itself contain embedded references. Thus a parameter reference may resolve to a parameter value that contains references. This allows the values of database fields to contain references to user defined parameters that are set as desired in the configuration file without needing to change the contents of database tables to effect the change.

Reference resolved values in Procedure fields allow dynamic definition of procedure attributes. References that are unresolved are fatal to Conductor unless the Unresolved_Reference configuration parameter has been set to a substitute string (e.g. ""). References that have incorrect syntax (e.g. unbalanced curly brace enclosures) are always fatal to Conductor.

Procedure Execution

The Command_Line value is reference resolved and {@linkplain #Parse_Command_Line parsed} into an initial command name and command arguments. An empty or NULL Command_Line is fatal. Before each procedure is run the log file is written with the {@link #PROCEDURE_LOG_DELIMITER} line. This is followed by a date and time stamp, the Sequence number, the Description field value (if it is not empty), and then the command line to be executed.

The command name and arguments are passed to the Java Runtime for execution as a Process by the host operating system. Note: the command is not run in a shell. It is, however, quite appropriate to run shell, or any other interpreted language, scripts (e.g. PERL). The only restriction on the procedure to be run is that it is accessible and executable. If the procedure can not be executed for any reason the source record's Status field value is appended with Conductor's {@link #NO_PROCEDURE} error status. Otherwise the Status field is updated with the host system Process ID for the executed procedure; this is always an integer value greater than 1 that uniquely identifies the executing procedure in the host operating system.

All standard output from the procedure is copied into the log file with an annotation before each line that indicates whether the source is the procedure's stdout or stderr streams. Because these are separate streams read by asynchronous threads attached to each process stream there can be no guarantee of the relative logging order of lines from the two sources; while each stream is always logged in the order in which the procedure output to it, the uncertainties of system stream buffering and thread scheduling are likely to result in lines from stdout appearing in the log before or after where they might occur relative to stderr lines appearing in a shell terminal listing.

Conductor waits for the procedure to complete before proceeding. However, it will not wait longer than the number of seconds from the Time_Limit field (which may have embedded references and may be a mathematical expression). If the value is NULL, empty or zero then there is no limit to the amount of time Conductor will wait for the procedure to complete. It is generally a good idea to place a maximum running time limit on any procedure that could become "hung" (for example in a loop or on an inaccessible). If the time limit is reached Conductor will destroy the procedure. This is done by sending the procedure a terminate signal (SIGTERM). This signal can be caught by the procedure so it has an opportunity to clean up open files or child processes of its own. For scripts that have launched long running computational programs it is correct practice to catch the terminate signal and halt these programs; failure to do so is likely to leave these child programs running as orphans. If a procedure does not catch the terminate signal it will be automatically terminated by the operating system. If Conductor must terminate a procedure due to a timeout the source record's Status field will be updated with Conductor's {@link #PROCEDURE_TIMEOUT} error status and the log will be written with notice of the timeout. If the procedure completes normally, then the standard output streams are drained and copied to the log file and the exit status from the procedure is also noted in the log file.

Procedure Status

When the procedure execution is done the Completion_Number parameter is updated. This will be a negative value if the procedure did not run to completion (could not be executed or exceed the Time_Limit), otherwise it will be the procedure's exit status value.

When a procedure completes normally Conductor uses either the Success_Status or Success_Message field values to determine if the procedure completed successfully. Usually the exit status is set by the procedure to a value that indicates if it succeeded. However, it may be necessary to examine the output of the procedure if the exit status is not reliable. There may also be unfortunate cases where there is no reliable indicator and all that can be done is assume that because the procedure completed it was successful.

If neither the Success_Status or Success_Message field values has been set to a non-empty value and an Empty_Success_Any configuration parameter was found with a "true" value then the procedure success of the proedure is implied (i.e. in this case the procedure is always successful if it completes normally); otherwise the Success_Status value is asserted to be "0".

If the Success_Status field is not empty it is reference resolved. The result is evaluated as a logical expression and if a result is obtained it determines if the procedure.was successful. Typically, the expression uses a reference to the Completion_Number parameter. The logical operators &, |, ~, =, <, >, <>, <=, >= may be used. The words "and", "or", and "not" can be used in place of &, |, and ~. Caution: Use &, not &&; |, not ||; ~, not !; =, not ==; and <>, not !=. A logical expression may contain embedded numeric expressions as well.

If the Success_Status does not contain a valid logical expression it is evaluated as a numeric expression. If the result, cast as an integer value, is equal to the procedure's exit status, then the procedure succeeded; otherwise it failed. A numeric expression may simply be a constant value (the symbols "pi" and "e" are recognized as constants) or may use the +, -, *, /, ^ operators; ** may be used instead of the ^ exponentiation operator. The tertiary operator ? with : may be used following an embedded logical expression such that if the logical expression is true then the following value before the : is used, else the value after the : is used (e.g. (4<5)?1:2 evaluates to 1). The functions sin, cos, tan, cot, sec, csc, arcsin, arccos, arctan, exp, ln, log2, log10, sqrt, cubert, abs, round, floor, ceiling, trunc may also be used with their argument following inside parentheses.

The Success_Message, if not empty, is used if the Success_Status is empty. It is referenced resolved and then matched, as a {@linkplain java.util.regex.Pattern regular expression}, against what was obtained from the procedure's stdout and stderr. If there is a match with either output, then the procedure succeeded; otherwise it failed. A resolved Success_Message value that does not produce a valid regular expression is fatal to Conductor. Note: Regular expressions are very powerful expression matching syntax similar to that used by PERL, but also can be daunting to the beginner.

Regardless of the outcome of procedure execution the Status field of the source record is updated in the database with the procedure's {@linkplain #Status_Indicators status indicator}. This indicator always includes the Conductor completion code which can be translated into a descriptive line of text by the {@link #Status_Conductor_Code_Description(int) Status_Conductor_Code_Description} static method. If the procedure completed with an exit status that value is included in the status indicator. The meaning of this value is procedure dependent. Of course the log file is also annotated accordingly.

On Failure

When Conductor determines that the procedure completed successfully it repeats the procedure execution operation with the next procedure in the pipeline Sequence. If Conductor determines that the procedure did not complete successfully, then it resolves any embedded references in the On_Failure field value and uses that as a command line for a procedure to be executed. This procedure is executed without any time limit. In the log file, where a normal procedure would have a {@link #PROCEDURE_LOG_DELIMITER} the On_Failure procedure has an {@link #ON_FAILURE_PROCEDURE_LOG_DELIMITER} and no Description. Although the completion status of this procedure is not included in the final status indicator of the source record's Status field it is noted in the log file.

When the number of sequential source processing failures reaches the Stop_on_Failure amount further processing is halted after the On_Failure procedure has been run.

When operating under the direction of a Manager (local and/or remote) if the Conductor halts for any reason it will send an email message to its Notify list and then wait to be told to start processing again. Without the possibility of a Manager to take charge the Conductor will exit after halting.

Sources Completion

The completion of the last of the Procedures in pipeline sequence, or the first On_Failure procedure, completes the processing of a source record. The log file is now closed. While there is another source record in the cache Conductor will continue trying to acquire an exclusive database lock. Once the cache is exhausted Conductor refreshes it from the Sources table with any new unprocessed records. If no unprocessed records are available then Conductor will sleep for the number of seconds indicated by the Poll_Interval configuration parameter. If no Poll_Interval parameter is present the default interval of 30 seconds is used. If the interval is less than or equal to 0, then Conductor processing will stop when it can find no source records to process.

System Dependencies

Java

Conductor is known to compile and run correctly with Java 1.4 and 1.5. Java was chosen for the implementation to maximize portability: as long as the host system provides a standard Java environment it should be able to run Conductor.

Conductor_ID

The Conductor_ID value will include the process ID of Conductor if it is available. Obtaining the process ID (PID) of Conductor - i.e. the Java Virtual Machine (JVM) that runs the Java classes - requires using a Java Native Interface (JNI) to the host system function that provides this information. Though this is trivial to implement it is outside the pure Java implementation of Conductor. Without the JNI code the Conductor_ID will only be the hostname of the system on which Conductor is running. With the JNI code the Conductor_ID will include the JVM PID after the hostname separated by a colon (':') character. The availability of the JVM PID will not have any effect on Conductor's operation. However, it is quite useful to have the JVM PID for procedures to use in disambiguating filenames in a parallel processing shared storage environment, and it can assist in systems administration work. A Native_Methods.c file that provides JNI access to the required system function is included in the source code distribution of Conductor. When the Conductor source code is compiled Native_Methods.c is also compiled to produce a dynamically loadable Native_Methods.so (or .jnilib on Apple OS X/Darwin systems) shared object library file in the Conductor/. subdirectory, where is the name of the host operating system (e.g. Darwin, FreeBSD, Linux or SunOS) and is the host system hardware architecture (e.g. i386, powerpc, x86_64 or sparc). N.B.: The library file must be copied to a location on the host system where it can be found by the dynamic linker (e.g. /usr/local/lib) when Conductor runs. The JNI library file can be built separately from the Java class files - if, for example, multiple operating systems and/or architecures are being used - by running "make jni" (GNU make may be named gmake) in the Conductor source code directory. The Native_Methods.c file requires the $(JNI_ROOT)/include/jni.h file included with the Java Software Development Kit (SDK) distribution. $(JNI_ROOT) is /usr/java by default, but a JNI_ROOT environment variable can be set with an alternative location before the JNI library is compiled.

@author Bradford Castalia, Christian Schaller - UA/PIRL @version 2.47 @see PIRL.Database @see PIRL.Conductor.Maestro */ public class Conductor implements Management { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Conductor (2.47 2012/04/16 06:04:09)"; // Conductor process identification. private static String Conductor_ID; // Configuration: /** The default configuration filename. */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Conductor.conf"; static { Configuration.Default_Source (DEFAULT_CONFIGURATION_FILENAME); } /** The Configuration object containing the configuration parameters. */ protected Configuration The_Configuration = null; /** The default {@link Configuration#Duplicate_Parameter_Action(int) action} should a duplicate parameter pathname occur in the Conductor Configuration file. */ public static final int DEFAULT_DUPLICATE_PARAMETER_ACTION = Configuration.PREFER_LAST_PARAMETER; /** Conductor Configuration parameters. */ public static final String CONDUCTOR_GROUP = "Conductor", // The name of parameters in the CONDUCTOR_GROUP - // Conductor: CONFIGURATION_SOURCE_PARAMETER = "Configuration_Source", DATABASE_SERVER_NAME_PARAMETER = "Database_Server_Name", DATABASE_SERVER_PARAMETER = Database.SERVER, HOSTNAME_PARAMETER = "Hostname", DATABASE_HOSTNAME_PARAMETER = "Database_Hostname", DATABASE_TYPE_PARAMETER = "Database_Type", PIPELINE_PARAMETER = "Pipeline", CATALOG_PARAMETER = "Catalog", PROCEDURES_TABLE_PARAMETER = "Procedures_Table", SOURCES_TABLE_PARAMETER = "Sources_Table", UNRESOLVED_REFERENCE_PARAMETER = "Unresolved_Reference", UNRESOLVED_REFERENCE_THROWS = "THROW", EMPTY_SUCCESS_ANY_PARAMETER = "Empty_Success_Any", MIN_SOURCE_RECORDS_PARAMETER = "Min_Source_Records", MAX_SOURCE_RECORDS_PARAMETER = "Max_Source_Records", POLL_INTERVAL_PARAMETER = "Poll_Interval", SOURCE_AVAILABLE_TRIES_PARAMETER = "Source_Available_Tries", STOP_ON_FAILURE_PARAMETER = "Stop_on_Failure", RECONNECT_TRIES_PARAMETER = "Reconnect_Tries", RECONNECT_DELAY_PARAMETER = "Reconnect_Delay", NOTIFY_PARAMETER = "Notify", // Source: SOURCE_NUMBER_PARAMETER = "Source_Number", SOURCE_PATHNAME_PARAMETER = "Source_Pathname", SOURCE_ID_PARAMETER = "Source_ID", CONDUCTOR_ID_PARAMETER = "Conductor_ID", SOURCE_DIRECTORY_PARAMETER = "Source_Directory", SOURCE_FILENAME_PARAMETER = "Source_Filename", SOURCE_FILENAME_ROOT_PARAMETER = "Source_Filename_Root", SOURCE_FILENAME_EXTENSION_PARAMETER = "Source_Filename_Extension", LOG_PATHNAME_PARAMETER = "Log_Pathname", LOG_DIRECTORY_PARAMETER = "Log_Directory", LOG_FILENAME_PARAMETER = "Log_Filename", SOURCE_SUCCESS_COUNT = "Source_Success_Count", SOURCE_FAILURE_COUNT = "Source_Failure_Count", TOTAL_FAILURE_COUNT = "Total_Failure_Count", // Procedure: TOTAL_PROCEDURE_RECORDS_PARAMETER = "Total_Procedure_Records", PROCEDURE_COUNT_PARAMETER = "Procedure_Count", PROCEDURE_SEQUENCE_PARAMETER = "Sequence", PROCEDURE_COMPLETION_NUMBER_PARAMETER = "Completion_Number", // Stage_Manager: REQUIRE_STAGE_MANAGER_PARAMETER = "Stage_Manager/Require_Stage_Manager", STAGE_MANAGER_PASSWORD_PARAMETER = "Stage_Manager/" + PIRL.Conductor.Maestro.Stage_Manager.PASSWORD_PARAMETER_NAME, STAGE_MANAGER_PORT_PARAMETER = "Stage_Manager/" + PIRL.Conductor.Maestro.Stage_Manager.PORT_PARAMETER_NAME, STAGE_MANAGER_TIMEOUT_PARAMETER = "Stage_Manager/Timeout", HELLO_PORT_PARAMETER_NAME = "Stage_Manager/" + PIRL.Conductor.Maestro.Stage_Manager.HELLO_PORT_PARAMETER_NAME, HELLO_ADDRESS_PARAMETER_NAME = "Stage_Manager/" + PIRL.Conductor.Maestro.Stage_Manager.HELLO_ADDRESS_PARAMETER_NAME; // Database: private String Database_Server_Name = null; /** The Database object used to access the database server. */ protected Database The_Database = null; // Number of database connection retries before giving up. private int Reconnect_Tries = 16, Reconnect_Delay = 300; // seconds. /** The name of the database catalog containing the pipeline tables. */ protected String Catalog = null; // This is static so it can be used by Exit if construction fails. /** The name of the pipeline (.) being managed. */ protected static String Pipeline = null; /** The Reference_Resolver object being used. */ protected Reference_Resolver Resolver = null; /** The default value to be used by the {@link Reference_Resolver} if a reference can not be resolved.

A null value means that the Reference_Resolver will throw an exception if a reference can not be resolved.

This value may be overriden by the {@link #UNRESOLVED_REFERENCE_PARAMETER} of the configuration file. A parameter value of {@link #UNRESOLVED_REFERENCE_THROWS} (case insensitive) is equivalent to a null default value. */ public static final String RESOLVER_DEFAULT_VALUE = null; // Throws an exception. // Mathematical expression parser (JCM). private Parser Expression_Parser = new Parser (); // Sources: /** Sources table field names. */ public static final String[] SOURCES_FIELD_NAMES = { SOURCE_NUMBER_PARAMETER, SOURCE_PATHNAME_PARAMETER, SOURCE_ID_PARAMETER, CONDUCTOR_ID_PARAMETER, "Status", LOG_PATHNAME_PARAMETER }; /** Sources table fields indexes. */ public static int SOURCE_NUMBER_FIELD = 0, SOURCE_PATHNAME_FIELD = 1, SOURCE_ID_FIELD = 2, CONDUCTOR_ID_FIELD = 3, STATUS_FIELD = 4, LOG_PATHNAME_FIELD = 5; private Fields_Map Sources_Map = null; /** Sources table name suffix. */ public static final String SOURCES_TABLE_NAME_SUFFIX = "_Sources"; private String Sources_Table = null; private Vector> // Cache of potential source records for processing. Source_Records = new Vector> (0); private Vector // Current source record being processed. Source_Record = null, // Current source record Status field values. Source_Status = null; /** The minimum value for the Max_Source_Records value. The value may be set in the configuration by the {@link #MIN_SOURCE_RECORDS_PARAMETER}. The default is {@link #MIN_SOURCE_RECORDS_DEFAULT}.

When operating in batch mode, this value control the minimum number of source records to be processed before stopping.

@see #MAX_SOURCE_RECORDS_DEFAULT */ public static final int MIN_SOURCE_RECORDS_DEFAULT = 1; private int Min_Source_Records = MIN_SOURCE_RECORDS_DEFAULT; /** The maximum number of unprocessed source records that will be obtained when the Conductor cache is refreshed.

This value may be set in the configuration by the {@link #MAX_SOURCE_RECORDS_PARAMETER}. The default is {@link #MAX_SOURCE_RECORDS_DEFAULT}.

@see #MIN_SOURCE_RECORDS_DEFAULT */ public static final int MAX_SOURCE_RECORDS_DEFAULT = 1000; private int Max_Source_Records = MAX_SOURCE_RECORDS_DEFAULT; /** Default number of source file availability tests.

Due to NFS filesystems latency it is possible that a source file that has just been created and registered in a Sources table by a Conductor on one system will not appear if accessed too soon by a Conductor on another system. The workaround is to repeatedly try to see the file, with a ten second delay between tries. The difficulty is knowing how many tries to make before giving up. As of this writing it seems that sufficient tries should be allowed for up to two minutes of trying.

The Configuration {@link #SOURCE_AVAILABLE_TRIES_PARAMETER} can be used to override the default. */ public static final int SOURCE_AVAILABLE_TRIES_DEFAULT = 12; /** Maximum number of source file availability tests. A maximum of 30 minutes of trying is allowed.

@see #SOURCE_AVAILABLE_TRIES_DEFAULT */ public static final int SOURCE_AVAILABLE_TRIES_MAX = 180; /** When the {@link #SOURCE_AVAILABLE_TRIES_PARAMETER} is this value source file availability confirmation is disabled. */ public static final int SOURCE_AVAILABLE_NO_CHECK = -1; private int Source_Available_Tries = SOURCE_AVAILABLE_TRIES_DEFAULT; // Source record processing statistics. private long Batch_Sources_Count = 0, Source_Success_Count = 0, Source_Failure_Count = 0, Total_Failure_Count = 0; // Time, in seconds. to wait between trying for new Sources. /** The polling interval, in seconds, for unprocessed source records when no unprocessed source records are obtained from the Sources table.

This value may be set by the configuration {@link #POLL_INTERVAL_PARAMETER}.

A value of zero sets batch mode: Conductor is to stop when no unprocessed source records are available and at least Min_Source_Records have been processed. If polling for more sources is required in batch mode a polling interval of {@link #BATCH_POLL_INTERVAL} seconds will be used.

@see #MIN_SOURCE_RECORDS_DEFAULT */ public static final int DEFAULT_POLL_INTERVAL = 30, BATCH_POLL_INTERVAL = 5; private static final int UNSET_INTEGER_VALUE = -999999999; private volatile int Poll_Interval = DEFAULT_POLL_INTERVAL, Poll_Interval_Override = UNSET_INTEGER_VALUE; // Procedures: /** Procedures table field names. */ public static final String[] PROCEDURES_FIELD_NAMES = { PROCEDURE_SEQUENCE_PARAMETER, "Description", "Command_Line", "Success_Status", "Success_Message", "Time_Limit", "On_Failure" }; /** Procedures table fields indexes. */ public static final int SEQUENCE_FIELD = 0, DESCRIPTION_FIELD = 1, COMMAND_LINE_FIELD = 2, SUCCESS_STATUS_FIELD = 3, SUCCESS_MESSAGE_FIELD = 4, TIME_LIMIT_FIELD = 5, ON_FAILURE_FIELD = 6; protected Fields_Map Procedures_Map = null; /** Procedures table name suffix. */ public static final String PROCEDURES_TABLE_NAME_SUFFIX = "_Procedures"; /** The name of the pipeline procedures table in the database. */ protected String Procedures_Table = null; /** The content of the pipeline procedures table, without the field names, sorted by sequence number. */ protected Vector> Procedure_Records = new Vector> (0); // A copy of the procedure record currently being used. private Vector Procedure_Record = null; private boolean Procedure_has_Status; private static String COMMAND_LINE_ARGUMENTS_DELIMITERS = " \t\n\r"; // Booleans for Run_Procedure. private static final boolean NORMAL = true, ON_FAILURE = false; /** Conductor procedure completion code.

@see #Status_Indicators(String) */ public static final int PROCEDURE_SUCCESS = 0, PROCEDURE_FAILURE = 1, INACCESSIBLE_FILE = -1, UNRESOLVABLE_REFERENCE = -2, NO_PROCEDURE = -3, PROCEDURE_TIMEOUT = -4, BAD_REGEX = -5, INVALID_DATABASE_ENTRY = -6; /** Conductor status failure code descriptions.

@see #Status_Conductor_Code_Description */ public static final String[] FAILURE_DESCRIPTION = { "Inaccessable file", "Unresolvable reference in Procedures table", "Procedure could not be executed", "Procedure timeout", "Invalid regular expression", "Invalid database table entry" }; /** The default for whether or not empty Success_Status and Success_Message fields in a procedure definition may imply any completion of the procedure is a success.

This value may be overridden by the {@link #EMPTY_SUCCESS_ANY_PARAMETER} configuruation parameter. */ public static final boolean DEFAULT_EMPTY_SUCCESS_ANY = false; private boolean Empty_Success_Any = DEFAULT_EMPTY_SUCCESS_ANY; // Pipeline processing: private Thread Processor = null, Main_Thread_Waiting = null; /** The default maximum number of sequential source processing failures. */ public static final int DEFAULT_STOP_ON_FAILURE = 0; private volatile int Stop_on_Failure = DEFAULT_STOP_ON_FAILURE, Stop_on_Failure_Override = UNSET_INTEGER_VALUE, Sequential_Failures = 0; /** Processing state: Source records are being processed. */ public static final int RUNNING = 3; /** Processing state: No unprocessed source records are available and the {@link #Poll_Interval() poll interval} for new records is positive.

Between attempts to {@link #Load_Source_Records() load new source records} that have not been unprocessed the processing thread will sleep for the poll interval. Once additional unprocessed source records have been obtained the {@link #RUNNING} state will be entered, unless the {@link #RUN_TO_WAIT} state has been set. */ public static final int POLLING = 2; /** Processing state: When the current source record completes processing the {@link #WAITING} state will be entered unless a failure condition caused the {@link #HALTED} state to occur.

N.B.: If the Conductor is repeatedly trying to confirm the existence of a source file or to reconnect to the database when the stop request is received retrying will be discontinued which can be expected to result in a source record processing failure or database connectivity failure halt.

This state is only entered when the user requests that processing {@link #Stop() stop} while the {@link #RUNNING} or {@link #POLLING} state is in effect. */ public static final int RUN_TO_WAIT = 1; /** Processing state: Idle; waiting for a {@link #Start() start} request. */ public static final int WAITING = -1; /** Processing state: A failure condition caused processing to halt.

The problem may be the result of the maximum number of sequential source records processing procedure failures having occured, a database access failure, or some other system error. This state remains in effect until a {@link #Start() start} request is received. */ public static final int HALTED = -2; private volatile int Processing_State = WAITING; // Indicates that the pipeline processing thread is sleeping. private volatile boolean Sleeping = false; // Any exception that occurs in the processing thread run. private Exception Processing_Exception = null; private Vector Processing_Listeners = new Vector (); // Miscellaneous. private static final char FILE_SEPARATOR = File.separatorChar, FILE_SEPARATOR_SUBSTITUTE = '%'; protected static final String NL = System.getProperty ("line.separator"); // Logging: private Styled_Multiwriter Logger = new Styled_Multiwriter (); // Log file. private String Log_File_Directory = null; private Writer Log_File_Writer = null; // Procedure output. private Stream_Logger stdout_Logger = null, stderr_Logger = null; /** Prefix applied to procedure stdout lines. */ public static final String STDOUT_NAME = "stdout"; /** Prefix applied to procedure stderr lines. */ public static final String STDERR_NAME = "stderr"; /** Marks the beginning of source file processing in a log file. */ public static final String SOURCE_FILE_LOG_DELIMITER = "======================================================================" + NL; /** Marks the beginning of procedure processing in a log file. */ public static final String PROCEDURE_LOG_DELIMITER = "----------------------------------------------------------------------" + NL; /** Marks the beginning of On_Failure procedure processing in a log file. */ public static final String ON_FAILURE_PROCEDURE_LOG_DELIMITER = "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" + NL; private static final SimpleAttributeSet NOTICE_STYLE = new SimpleAttributeSet (), HIGHLIGHT_STYLE = new SimpleAttributeSet (), MARKER_STYLE = new SimpleAttributeSet (); static { StyleConstants.setBold (NOTICE_STYLE, true); StyleConstants.setForeground (NOTICE_STYLE, Colors.NOTICE_STYLE); StyleConstants.setForeground (HIGHLIGHT_STYLE, Colors.HIGHLIGHT_STYLE); StyleConstants.setFontFamily (MARKER_STYLE, "Monospaced"); StyleConstants.setBold (MARKER_STYLE, true); } // Exit status: /** Conductor success exit status (0). */ public static final int EXIT_SUCCESS = 0; /** Command line syntax problem exit status (1). */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** Configuration problem exit status (2). */ public static final int EXIT_CONFIGURATION_PROBLEM = 2; /** Configuration problem exit status (3). */ public static final int EXIT_DATABASE_PROBLEM = 3; /** I/O failure exit status (4). */ public static final int EXIT_IO_FAILURE = 4; /** The number of sequential source processing failures reached the Stop-on-Failure amount. */ public static final int EXIT_TOO_MANY_FAILURES = 6; /** The {@link #Require_Stage_Manager required} Stage_Manager connection could not be established. */ public static final int EXIT_STAGE_MANAGER = 7; /** An unexpected exception occured (9). */ public static final int EXIT_UNEXPECTED_EXCEPTION = 9; // This is static so it can be used by Exit if construction fails. /** A list of recipients to receive notification of Conductor failure.

The list is obtained from the Configuration {@link #NOTIFY_PARAMETER}. */ private static Vector Notify_List = null; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_DATABASE = 1 << 3, DEBUG_PIPELINE = 1 << 4, DEBUG_GET_SOURCE = 1 << 5, DEBUG_PROCESS_SOURCE = 1 << 6, DEBUG_SET_PARAMETER = 1 << 7, DEBUG_PROCEDURE_STATUS = 1 << 8, DEBUG_RUN_PROCEDURE = 1 << 9, DEBUG_PARSE_COMMAND_LINE = 1 << 10, DEBUG_TABLE_UPDATE = 1 << 11, DEBUG_DATABASE_CONNECTION = 1 << 12, DEBUG_MESSAGES = 1 << 13, DEBUG_MANAGEMENT = 1 << 14, DEBUG_PROCESSING_EVENT = 1 << 15, DEBUG_NOTIFICATION = 1 << 16, DEBUG_EXIT = 1 << 17, DEBUG_TO_FILE = 1 << 18, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; private static final String DEBUG_FILENAME_BASE = "Conductor"; private static String DEBUG_Filename = null; private static final PrintStream stdout = System.out; /*============================================================================== Constructors */ /** Construct a Conductor for a pipeline from a Configuration.

The Configuration object is expected to contain all the necessary information Conductor needs to connect to the database as well as any other Conductor parameters it might use.

@param pipeline The name of the pipeline to be managed. @param configuration A Configuration object. @param database_server_name The name of a Configuration Group that will provide the database server access parameters. @throws Database_Exception if there is a problem connecting to the database. @throws Configuration_Exception if there is a problem with the configuration file. @throws IOException if a connection could not be made to the Stage_Manager and {@link #Require_Stage_Manager} is true; */ public Conductor ( String pipeline, Configuration configuration, String database_server_name ) throws Database_Exception, Configuration_Exception, IOException { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Conductor"); Pipeline = pipeline; // Setup the initial Configuration. The_Configuration = Preconfigure (configuration); // Establish the Database connection. try { The_Database = new Database (The_Configuration); if ((Database_Server_Name = database_server_name) == null) Database_Server_Name = The_Database.Default_Server_Name (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Connecting to Database Server " + Database_Server_Name); The_Database.Connect (Database_Server_Name); } catch (Database_Exception exception) { throw Database_Error ("Unable to connect to the database." + NL + exception.getMessage ()); } // Construct the Reference_Resolver. Resolver = new Reference_Resolver (The_Database); // Update basic configuration parameters. try {Postconfigure (The_Configuration);} catch (Configuration_Exception exception) { try {The_Database.Disconnect ();} catch (Database_Exception except) {} throw exception; } // Initialize the Procedures and Sources records. Load_Procedure_Records (); Load_Source_Records (); // Establish a connection to the Stage_Manager. String report = Connect_to_Stage_Manager (); if (report != null) stdout.println (report); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Conductor"); } /** Construct a Conductor for a pipeline from a Configuration.

The Configuration object is expected to contain all the necessary information Conductor needs to connect to the database as well as any other Conductor parameters it might use.

The database server access parameters will be obtained from the Configuration Group named by the first entry in the Server parameter list.

@param pipeline The name of the pipeline to be managed. @param configuration A Configuration object. @throws Database_Exception if there is a problem connecting to the database. @throws Configuration_Exception if there is a problem with the configuration file. @throws IOException if a connection could not be made to the Stage_Manager and {@link #Require_Stage_Manager} is true; */ public Conductor ( String pipeline, Configuration configuration ) throws Database_Exception, Configuration_Exception, IOException {this (pipeline, configuration, null);} /** Constructs an uninititalized Conductor. */ protected Conductor () {} /*============================================================================== Configuration */ /** Set the effective configuration.

If no Configuration is provided an effort is made to load the {@link #DEFAULT_CONFIGURATION_FILENAME}.

Essential configuration information is obtained from, and set in, the {@link #CONDUCTOR_GROUP} parameters of the Configuration:

{@link Local_Theater#CLASS_ID_PARAMETER_NAME} - Set
The {@link #ID} of this class.
{@link #CONFIGURATION_SOURCE_PARAMETER} - Set
The {@link Configuration#Source() source} of the Configuration that is being used.
{@link #HOSTNAME_PARAMETER} - Set
The {@link Host#FULL_HOSTNAME} of the host system.
{@link #CONDUCTOR_ID_PARAMETER} - Set
The identifying name of this Conductor. This will be the {@link Host#SHORT_HOSTNAME} of the system. If the host system {@link #PID() process ID} can be obtained it is appended to the hostname after a colon (':') delimiter.
{@link #NOTIFY_PARAMETER} - Get
A list of email address to be sent a {@link Notify} message if Conductor stops because the {@link #STOP_ON_FAILURE_PARAMETER} limit has been reached or an exception has been thrown. If this parameter is not present or has an empty value no notification message will be sent.
{@link #REQUIRE_STAGE_MANAGER_PARAMETER} - Get and Set
A {@link Configuration#Enabled(String, boolean) flag} that determines if a connection to a Stage_Manager is required for this Conductor. N.B.: This parameter is only read once when the Conductor is first configured; when the Conductor is reconfigured on being restarted after a Stop or Halt state the initial value is reset in the internal Configuration regardless of any change to the external configuration source. Default: {@link #Require_Stage_Manager}.
{@link #STAGE_MANAGER_PORT_PARAMETER} - Get and Set
The port number to use for connecting to a Stage_Manager. Default: {@link PIRL.Conductor.Maestro.Local_Theater#Default_Port() the default port for Theater Management}.
{@link #STAGE_MANAGER_TIMEOUT_PARAMETER} - Get and Set
The amount of time (seconds) to wait for a Stage_Manager connection to complete before the connection attempt fails.
{@link #STAGE_MANAGER_PASSWORD_PARAMETER} - Get
The password required to authenticate a Stage_Manager connection. If this parameter is present its value is masked out in the internal Configuration. If this parameter is not present, or its value is empty, and the Stage_Manager requires an authenticated connection the connection attempt will fail.
{@link #HELLO_PORT_PARAMETER_NAME} - Get and Set
The port number to use when listening for a Stage_Manager "Hello" broadcast that it is ready for connections.
{@link #HELLO_ADDRESS_PARAMETER_NAME} - Get and Set
The multicast address to use when listening for a Stage_Manager "Hello" broadcast that it is ready for connections.
{@link #SOURCE_SUCCESS_COUNT} - Set
The number of source records that have been successfully processed since this Conductor was started.
{@link #SOURCE_FAILURE_COUNT} - Set
The number of source records that have resulted in a failed procedure condition during processing.
{@link #TOTAL_FAILURE_COUNT} - Set
The total number of source records that failed to be processed for any reason.

@param configuration The Configuration to be used. If null the {@link #DEFAULT_CONFIGURATION_FILENAME} will be tried. If that fails the filename will be qualified relative to the location of this class in the CLASSPATH. @return The Configuration that was used. This will be the same as the specified configuation if it was not null; otherwise it will be the default configuration that was loaded. @throws Configuration_Exception If the specified configuration is null and a default configuration source could not be found and loaded; or there was a problem setting a configuration value. */ protected Configuration Preconfigure ( Configuration configuration ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Conductor.Preconfigure"); if (configuration == null) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Trying " + Configuration.Default_Source ()); try {configuration = new Configuration ((String)null);} catch (IllegalArgumentException exception) { throw new Configuration_Exception (ID + NL + "Unable to find the configuration file \"" + Configuration.Default_Source () + "\"."); } } // Reset the default configuration source to the current source. Configuration.Default_Source (configuration.Source ()); configuration.Case_Sensitive (false); // Done early so the value will be available for the DEBUG_Filename. Theater_Management.Default_Port ((int)configuration.Get_Number (Config_Pathname (STAGE_MANAGER_PORT_PARAMETER), Theater_Management.Default_Port ())); if (The_Configuration == null) { // First time initialization only. // ID of this Conductor process. Conductor_ID = Host.SHORT_HOSTNAME; int PID = PID (); if (PID != 0) Conductor_ID += ":" + PID; // Stage_Manager required? Require_Stage_Manager = configuration.Enabled (Config_Pathname (REQUIRE_STAGE_MANAGER_PARAMETER), Require_Stage_Manager); if ((DEBUG & DEBUG_TO_FILE) != 0) { if (DEBUG_Filename == null) { DEBUG_Filename = System.getProperty ("user.home") + File.separatorChar + DEBUG_FILENAME_BASE + '-' + Conductor_ID + '_' + Theater_Management.Default_Port () + ".DEBUG"; System.out.println ("*** Redirecting System.out to " + DEBUG_Filename); try { System.setOut (new PrintStream (new FileOutputStream (DEBUG_Filename), true)); } catch (IOException exception) { System.out.println ("!!! Unable to open " + DEBUG_Filename); } System.out.println (DEBUG_Filename + NL + ID + NL); } } } if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Conductor.Preconfigure: Input Configuration -" + NL + configuration.Description ()); // Class ID. Config_Value (configuration, Local_Theater.CLASS_ID_PARAMETER_NAME, ID); // ID of this Conductor process. Config_Value (configuration, CONDUCTOR_ID_PARAMETER, Conductor_ID); // Hostname. Config_Value (configuration, HOSTNAME_PARAMETER, Host.FULL_HOSTNAME); // Configuration source. Config_Value (configuration, CONFIGURATION_SOURCE_PARAMETER, configuration.Source ()); // Stage_Manager: Config_Value (configuration, REQUIRE_STAGE_MANAGER_PARAMETER, String.valueOf (Require_Stage_Manager)); Config_Value (configuration, STAGE_MANAGER_PORT_PARAMETER, new Integer (Theater_Management.Default_Port ())); Theater_Management.Receive_Timeout ((int)configuration.Get_Number (Config_Pathname (STAGE_MANAGER_TIMEOUT_PARAMETER), Theater_Management.Receive_Timeout ())); Config_Value (configuration, STAGE_MANAGER_TIMEOUT_PARAMETER, new Integer (Theater_Management.Receive_Timeout ())); Theater_Management.Key (Stage_Manager_Key = Config_Value (configuration, STAGE_MANAGER_PASSWORD_PARAMETER)); if (Stage_Manager_Key != null) // Mask out the Stage_Manager password, and only this password. Config_Value (configuration, STAGE_MANAGER_PASSWORD_PARAMETER, Processing_Changes.MASKED_PASSWORD); Theater_Management.Auto_Open (! Require_Stage_Manager, (int)configuration.Get_Number (Config_Pathname (HELLO_PORT_PARAMETER_NAME), Theater_Management.Hello_Listener_Port ()), Config_Value (configuration, HELLO_ADDRESS_PARAMETER_NAME)); Config_Value (configuration, HELLO_PORT_PARAMETER_NAME, new Integer (Theater_Management.Hello_Listener_Port ())); Config_Value (configuration, HELLO_ADDRESS_PARAMETER_NAME, Theater_Management.Hello_Listener_Address ()); // Who to notify on Conductor failure. Notify_List = configuration.Get (Config_Pathname (NOTIFY_PARAMETER)); if (Notify_List.isEmpty ()) Notify_List = null; else for (int index = 0; index < Notify_List.size (); index++) if (! (Notify_List.get (index) instanceof String)) throw new Configuration_Exception (Error_Message ("Unexpected subarray in the configuration " + NOTIFY_PARAMETER + " parameter at entry " + index + '.')); // Processing status values. Config_Value (configuration, SOURCE_SUCCESS_COUNT, Source_Success_Count); Config_Value (configuration, SOURCE_FAILURE_COUNT, Source_Failure_Count); Config_Value (configuration, TOTAL_FAILURE_COUNT, Total_Failure_Count); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Pre-Configuration -" + NL + configuration.Description () + NL +"<<< Conductor.Preconfigure"); return configuration; } /** Update the configuration and application control values after the Database and Reference_Resolver have been constructred.

The following {@link #CONDUCTOR_GROUP} parameters of the Configuration are affected:

{@link #DATABASE_SERVER_NAME_PARAMETER} - Set
The name of the Group of parameters that provided the database server access information used to establishs a {@link Database#Connect(String) connection}.
{@link #DATABASE_TYPE_PARAMETER} - Set
The Type of {@link PIRL.Database.Data_Port} class used to access the database server.
{@link #DATABASE_HOSTNAME_PARAMETER} - Set
The hostname where the database server is located.
{@link #CATALOG_PARAMETER} - Get and Set
The name of the database catalog containing the pipeline tables. This value is only obtained once from the initial Configuration, and then it is obtained from the {@link #CONDUCTOR_GROUP} only if it could not be obtained as part of the user specified pipeline name nor from the database access parameters of the {@link #DATABASE_SERVER_NAME_PARAMETER} Group. This is a required value. If it can not be obtained a Configuration_Exception will be thrown. It is always set in the specified configuration.
{@link #PIPELINE_PARAMETER} - Set
The name of the pipeline to be processed. This required parameter is user specified.
{@link #PROCEDURES_TABLE_PARAMETER} - Set
The name of the procedures definitions table in the database.
{@link #SOURCES_TABLE_PARAMETER} - Set
The name of the sources records table in the database.
{@link #RECONNECT_TRIES_PARAMETER} - Get and Set
The maximum number of database server {@link #Connect_to_Database() reconnect} tries to do if the database connection is lost.
{@link #UNRESOLVED_REFERENCE_PARAMETER} - Get and Set
The default value to be used by the {@link Reference_Resolver} if a reference can not be resolved. A parameter value of {@link #UNRESOLVED_REFERENCE_THROWS} (case insensitive) means that the Reference_Resolver will throw an exception if a reference can not be resolved. If the Configuration does not contain this parameter the {@link #RESOLVER_DEFAULT_VALUE} will be used; a null default value means that the Reference_Resolver will throw an exception if a reference can not be resolved.
{@link #LOG_PATHNAME_PARAMETER} or {@link #LOG_DIRECTORY_PARAMETER} - Get
The directory where log files will be written if a source record does not specify a log directory. The default log directory is the current working directory.
{@link #EMPTY_SUCCESS_ANY_PARAMETER} - Get and Set
A {@link Configuration#Enabled(String, boolean) flag} that determines whether or not empty Success_Status and Success_Message fields in a procedure definition may imply any completion of the procedure is a success. If this parameter is not present the {@link #DEFAULT_EMPTY_SUCCESS_ANY} value will be used.
{@link #MIN_SOURCE_RECORDS_PARAMETER} - Get and Set
The minimum number of unprocessed source records to be obtained from the database when the Conductor cache is refreshed. When Contuctor is operating in batch mode (the {@link #POLL_INTERVAL_PARAMETER polling interval} is zero) this value specifies the minimum number of source records to be processed before processing will stop. The minimum for this value is 1.
{@link #MAX_SOURCE_RECORDS_PARAMETER} - Get and Set
The maximum number of unprocessed source records to be obtained from the database when the Conductor cache is refreshed. This value is used to prevent excessive memory consumption when a large number of unprocessed source records are available. The minimum for this value is limited to Min_Source_Records.
{@link #SOURCE_AVAILABLE_TRIES_PARAMETER} - Get and Set
The maximum number of times to try and confirm that the Source_Pathname is available before the source record is processed. If this parameter is not present the {@link #SOURCE_AVAILABLE_TRIES_DEFAULT} will be used. The value is limited to be no more than {@link #SOURCE_AVAILABLE_TRIES_MAX}. If the value is negative it will be set to (@link #SOURCE_AVAILABLE_NO_CHECK} and source availability confirmation will be disabled. N.B. Source availability confirmation ensures that procedures will not fail due to a missing source file; disabling this confirmation would be appropriate for a pipeline that does not process a Source_Pathname, or for which the Source_Pathname is not a filename but some other information used by a procedure.
{@link #POLL_INTERVAL_PARAMETER} - Get and Set
The polling interval, in seconds, for unprocessed source records when the no unprocessed source records are obtained. A value of zero means that Conductor is to stop if when no unprocessed source records are available. If this parameter is not present the {@link #DEFAULT_POLL_INTERVAL} will be used.
{@link #STOP_ON_FAILURE_PARAMETER} - Get and Set
The number of sequential source record processing failures at which Conductor is to stop processing. A value less than or equal to zero means that processing is never to stop due to sequential failures. This parameter may have a boolean value which, if true, is equivalent to the number one; if false sequential failures will never cause processing to stop. If this parameter is not present the {@link #DEFAULT_STOP_ON_FAILURE} value will be used.

@param configuration The Configuration to be used. If null nothing is done. @throws Configuration_Exception If there was a problem setting a configuration value. */ protected void Postconfigure ( Configuration configuration ) throws Configuration_Exception { if (configuration == null) return; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Conductor.Postconfigure"); // Database information. if (Database_Server_Name != null) Config_Value (configuration, DATABASE_SERVER_NAME_PARAMETER, Database_Server_Name); Config_Value (configuration, DATABASE_TYPE_PARAMETER, The_Database .Data_Port () .Configuration () .Get_One (Database.TYPE)); Config_Value (configuration, DATABASE_HOSTNAME_PARAMETER, The_Database .Data_Port () .Configuration () .Get_One (Configuration.HOST)); if (Procedures_Table == null) { // First time initialization only. // Database catalog containing the tables. Catalog = null; try {Catalog = The_Database.Catalog_Name (Pipeline);} catch (Database_Exception exception) {} if (Catalog == null || Catalog.length () == 0) { // Try for a Catalog name from the Configuration Conductor group. if ((Catalog = Config_Value (configuration, CATALOG_PARAMETER)) == null && // Try for a Catalog name from the Database server group. (Catalog = The_Database.Configuration ().Get_One (CATALOG_PARAMETER)) == null) throw new Configuration_Exception (Error_Message ("The database catalog " + "containing the procedures and sources tables" + NL +"could not be determined from the pipeline name \"" + Pipeline + '"' + NL +"nor from a \"" + CATALOG_PARAMETER + "\" parameter in the " + configuration.Source () + " configuration file.")); try {Pipeline = The_Database.Table_Reference (Catalog, Pipeline);} catch (Database_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to assemble the fully qualified pipleline name" + NL +" from the catalog \"" + Catalog + "\" and pipeline \"" + Pipeline + "\" names." + NL + exception.getMessage ())); } } // Table references for the procedures and sources. try {Sources_Table = The_Database.Table_Reference (Catalog, Pipeline + SOURCES_TABLE_NAME_SUFFIX);} catch (Database_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to assemble the fully qualified sources table name" + NL +" from the catalog \"" + Catalog + "\" and sources \"" + Pipeline + SOURCES_TABLE_NAME_SUFFIX + "\" names." + NL + exception.getMessage ())); } try {Procedures_Table = The_Database.Table_Reference (Catalog, Pipeline + PROCEDURES_TABLE_NAME_SUFFIX);} catch (Database_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to assemble the fully qualified procedures table name" + NL +" from the catalog \"" + Catalog + "\" and procedures \"" + Pipeline + PROCEDURES_TABLE_NAME_SUFFIX + "\" names." + NL + exception.getMessage ())); } } Config_Value (configuration, CATALOG_PARAMETER, Catalog); try {Config_Value (configuration, PIPELINE_PARAMETER, The_Database.Table_Name (Pipeline));} catch (Database_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to extract the pipeline name from \"" + Pipeline + "\"." + NL + exception.getMessage ())); } Config_Value (configuration, SOURCES_TABLE_PARAMETER, Sources_Table); Config_Value (configuration, PROCEDURES_TABLE_PARAMETER, Procedures_Table); // Database reconnect parameters. if ((Reconnect_Tries = (int)configuration.Get_Number (Config_Pathname (RECONNECT_TRIES_PARAMETER), Reconnect_Tries)) < 0) Reconnect_Tries = 0; Config_Value (configuration, RECONNECT_TRIES_PARAMETER, new Integer (Reconnect_Tries)); if ((Reconnect_Delay = (int)configuration.Get_Number (Config_Pathname (RECONNECT_DELAY_PARAMETER), Reconnect_Delay)) < 0) Reconnect_Delay = 0; Config_Value (configuration, RECONNECT_DELAY_PARAMETER, new Integer (Reconnect_Delay)); // Unresolved reference default value. String default_value = Config_Value (configuration, UNRESOLVED_REFERENCE_PARAMETER); if (default_value == null) Config_Value (configuration, UNRESOLVED_REFERENCE_PARAMETER, ((default_value = RESOLVER_DEFAULT_VALUE) == null) ? UNRESOLVED_REFERENCE_THROWS : default_value); else if (default_value.toUpperCase ().startsWith (UNRESOLVED_REFERENCE_THROWS)) default_value = null; Resolver.Default_Value (default_value); // Log file pathname. Log_File_Directory = Config_Value (configuration, LOG_PATHNAME_PARAMETER); if (Log_File_Directory == null || Log_File_Directory.length () == 0) // Try for the alternate log directory parameter. Log_File_Directory = Config_Value (configuration, LOG_DIRECTORY_PARAMETER); // Empty Success fields may imply any completion is success. Empty_Success_Any = configuration.Enabled (Config_Pathname (EMPTY_SUCCESS_ANY_PARAMETER), DEFAULT_EMPTY_SUCCESS_ANY); Config_Value (configuration, EMPTY_SUCCESS_ANY_PARAMETER, String.valueOf (Empty_Success_Any)); // Minimum number of source records to process in batch mode. Min_Source_Records = (int)configuration.Get_Number (Config_Pathname (MIN_SOURCE_RECORDS_PARAMETER), MIN_SOURCE_RECORDS_DEFAULT); if (Min_Source_Records < 1) Min_Source_Records = 1; Config_Value (configuration, MIN_SOURCE_RECORDS_PARAMETER, new Integer (Min_Source_Records)); // Maximum number of source records to obtain in a select. Max_Source_Records = (int)configuration.Get_Number (Config_Pathname (MAX_SOURCE_RECORDS_PARAMETER), MAX_SOURCE_RECORDS_DEFAULT); if (Max_Source_Records < Min_Source_Records) Max_Source_Records = Min_Source_Records; Config_Value (configuration, MAX_SOURCE_RECORDS_PARAMETER, new Integer (Max_Source_Records)); // Source file availability retries. Source_Available_Tries = (int)configuration.Get_Number (Config_Pathname (SOURCE_AVAILABLE_TRIES_PARAMETER), SOURCE_AVAILABLE_TRIES_DEFAULT); if (Source_Available_Tries > SOURCE_AVAILABLE_TRIES_MAX) Source_Available_Tries = SOURCE_AVAILABLE_TRIES_MAX; if (Source_Available_Tries < 0) Source_Available_Tries = SOURCE_AVAILABLE_NO_CHECK; Config_Value (configuration, SOURCE_AVAILABLE_TRIES_PARAMETER, new Integer (Source_Available_Tries)); // Source records poll interval. Poll_Interval = (int)configuration.Get_Number (Config_Pathname (POLL_INTERVAL_PARAMETER), DEFAULT_POLL_INTERVAL); if (Poll_Interval < 0) Poll_Interval = 0; Config_Value (configuration, POLL_INTERVAL_PARAMETER, new Integer (Poll_Interval)); // When to stop processing after sequential failures. Stop_on_Failure = DEFAULT_STOP_ON_FAILURE; String text = Config_Value (configuration, STOP_ON_FAILURE_PARAMETER); if (text != null) { if (text.equalsIgnoreCase ("ENABLED") || text.equalsIgnoreCase ("TRUE") || text.equalsIgnoreCase ("YES") || text.equalsIgnoreCase ("ON") || text.equalsIgnoreCase ("Y")) Stop_on_Failure = 1; else { try { Stop_on_Failure = Integer.parseInt (text); if (Stop_on_Failure < 0) Stop_on_Failure = 0; } catch (NumberFormatException exception) {Stop_on_Failure = 0;} } } Config_Value (configuration, STOP_ON_FAILURE_PARAMETER, new Integer (Stop_on_Failure)); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Post-Configuration -" + NL + configuration.Description () + NL +"<<< Conductor.Postconfigure"); } private Configuration Reconfigure () throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Conductor.Reconfigure"); Configuration configuration = Preconfigure (null); Postconfigure (configuration); // Apply override values that are not unset. if (Poll_Interval_Override != UNSET_INTEGER_VALUE) Config_Value (configuration, POLL_INTERVAL_PARAMETER, new Integer (Poll_Interval = Poll_Interval_Override)); if (Stop_on_Failure_Override != UNSET_INTEGER_VALUE) Config_Value (configuration, STOP_ON_FAILURE_PARAMETER, new Integer (Stop_on_Failure = Stop_on_Failure_Override)); The_Configuration = configuration; Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration)); Resolver.Parameters (configuration); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Conductor.Reconfigure"); return configuration; } /** Get a String parameter value from the configuration.

The parameter is sought in the CONDUCTOR_GROUP, but defaults are acceptable.

If a value is found it is resolved for embedded references. An unresolved reference that would throw a Database_Exception results in a null value. A syntax error (ParseException) leaves the value unchanged.

@param name The name of the Assignment parameter for which to get a value. If not an absolute pathname an absolute pathname for the name in the {@link #CONDUCTOR_GROUP} is used. @return The first value String for the named parameter. This will be null if no parameter by that name exists, or it contained an unresolvable reference. */ protected String Config_Value ( String name ) {return Config_Value (The_Configuration, name);} private String Config_Value ( Configuration configuration, String name ) { name = configuration.Get_One (Config_Pathname (name)); if (name != null && Resolver != null) { try {name = Resolver.Resolve (name);} catch (Database_Exception exception) {name = null;} catch (ParseException exception) {} catch (Unresolved_Reference exception) {} } return name; } /** Set a parameter in the configuration.

The parameter is set in the {@link #CONDUCTOR_GROUP}.

@param name The name of the Assignment parameter to have its value set. If not an absolute pathname an absolute pathname for the name in the {@link #CONDUCTOR_GROUP} is used. @param value An Object to use for the parameter's value. : If null, the parameter will have no value; it will be a Token. @return true if an existing parameter by the same name was replaced; false if the parameter is being set for the first time. @throws Configuration_Exception If there was a problem setting the parameter. @see Configuration#Set(String, Object) */ protected boolean Config_Value ( String name, Object value ) throws Configuration_Exception {return Config_Value (The_Configuration, name, value);} private static boolean Config_Value ( Configuration configuration, String name, Object value ) throws Configuration_Exception { if ((DEBUG & DEBUG_SET_PARAMETER) != 0) System.out.println (">-< Conductor.Config_Value: " + name + " = " + value); try {return configuration.Set (Config_Pathname (name), value);} catch (Configuration_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to set configuration parameter: " + name + " = " + value + NL + exception.getMessage ())); } } /** Get an absolute Conductor Configuration pathname.

If the specified name is not an absolute pathname the {@link #CONDUCTOR_GROUP} is used as the root for to make an {@link Configuration#Absolute_Pathname(String, String) absolute pathname} from the name.

@param name The parameter name to be made absolute. @return An absolute Configuration pathname String. */ public static String Config_Pathname ( String name ) {return Configuration.Absolute_Pathname (CONDUCTOR_GROUP, name);} // Management implementation. /** Get the Conductor Configuration.

N.B.: All "password" (case insensitive) parameters will have their values masked out.

@return A Configuration containing a copy of the current Conductor Configuration. */ public Configuration Configuration () { Configuration configuration = null; try { configuration = new Configuration (The_Configuration); // Mask all password values. configuration.Case_Sensitive (false); configuration.Set_All ("PASSWORD", Processing_Changes.MASKED_PASSWORD); } catch (Configuration_Exception exception) { // This shouldn't be possible: the configuration has been vetted. configuration = null; } return configuration; } /** Gets the process ID for this application.

A native method is to be used to obtain the PID that uniquely identifies the current process on the host system.

@return A PID integer. This will be zero if a PID can not be obtained because the native method is not present. */ private int PID () {return Native_Methods.PID ();} /*============================================================================== Database */ /** Establish the Database connection.

The connection will be retried up to the number of times specified by the RECONNECT_TRIES_PARAMETER with the number of seconds delay between retries specified by the RECONNECT_DELAY_PARAMETER. Connection retries stop when the connection to the database is successful, the number of retries is exhausted, or an exception occurs that is not associated with a connection failure.

@return The last Database_Exception that occured. This will be null if the connection was successful. */ protected Database_Exception Connect_to_Database () { if ((DEBUG & DEBUG_DATABASE_CONNECTION) != 0) System.out.println (">>> Conductor.Connect_to_Databse"); int retry = 0; while (true) { try {The_Database.Disconnect ();} catch (Database_Exception exception) {} try { if ((DEBUG & DEBUG_DATABASE_CONNECTION) != 0) System.out.println (" Connecting to Database Server " + Database_Server_Name); The_Database.Connect (Database_Server_Name); if ((DEBUG & DEBUG_DATABASE_CONNECTION) != 0) System.out.println ("<<< Connect_to_Databse: Connected"); return null; } catch (Database_Exception exception) { if (Processing_State == RUN_TO_WAIT || ! exception.Disconnected () || retry >= Reconnect_Tries) return Database_Error ("Unable to connect to the database." + NL + retry + " retr" + ((retry == 1) ? "y" : "ies") + " attempted with " + Reconnect_Delay + " second delay." + NL + exception.getMessage ()); } catch (Configuration_Exception exception) { return Database_Error ("Unable to connect to the database." + NL + exception.getMessage ()); } if (Reconnect_Delay > 0) { if ((DEBUG & DEBUG_DATABASE_CONNECTION) != 0) System.out.println (" Connect_to_Databse: Sleeping " + Reconnect_Delay + " seconds"); Sleep (Reconnect_Delay); } ++retry; if ((DEBUG & DEBUG_DATABASE_CONNECTION) != 0) System.out.println (" Connect_to_Databse: Retry " + retry); } } /** Resolve a reference

If the reference can not be resolved because the Database has become disconnected the Database is {@link #Connect_to_Database() connected} and the reference resolution is tried again.

@param reference The reference String to be {@link Reference_Resolver#Resolve(String) resolved}. @return The resolved reference String. @throws Database_Exception If there was a problem accessing the Database. @throws ParseException If the reference contained a mathematical expression that could not be correctly parsed. @throws Unresolved_Reference If the reference could not be resolved and a non-null {@link #Resolver_Default_Value(String) default value} had not been assigned. @see Reference_Resolver */ protected String Resolve ( String reference ) throws Database_Exception, ParseException, Unresolved_Reference { try {return Resolver.Resolve (reference);} catch (Database_Exception exception) { if (exception.Disconnected ()) // Try to reconnect. Connect_to_Database (); else throw exception; return Resolver.Resolve (reference); } } // Management implementation. /** Set the Conductor default Reference_Resolver value.

Normally when the Conductor Reference_Resolver is unable to resolve a reference it will throw an {@link #Processing_Exception() exception} and enter the halted {@link #Processing_State() processing state}, or exit if it is not {@link #Connected_to_Stage_Manager() connected to a Stage_Manager} at the time. If, however, the {@link Reference_Resolver#Default_Value(String) Reference_Resolver default value} is set to a non-null String that value will be used for the unresolved_reference instead of throwing an exception.

If the specified value is different from the current Reference_Resolver default value the Configuration {@link #UNRESOLVED_REFERENCE_PARAMETER} is set to this value (or {@link #UNRESOLVED_REFERENCE_THROWS} if the value is null) and processing event notification with the updated Configuration is sent to all listeners.

@param value The default Reference_Resolver String value. If this starts with {@link #UNRESOLVED_REFERENCE_THROWS} (case insensitive) null will be used. @return This Conductor Management object. @see Reference_Resolver */ public Management Resolver_Default_Value ( String value ) { if (value.toUpperCase ().startsWith (UNRESOLVED_REFERENCE_THROWS)) value = null; String resolver_value = Resolver.Default_Value (); if ((resolver_value == null && value != null) || (resolver_value != null && ! resolver_value.equals (value))) { try { Config_Value (UNRESOLVED_REFERENCE_PARAMETER, (value == null) ? UNRESOLVED_REFERENCE_THROWS : value); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration)); synchronized (Resolver) {Resolver.Default_Value (value);} } catch (Configuration_Exception exception) {/* Don't set an unreportable value. */} } return this; } // Management implementation. /** Get the Conductor default Reference_Resolver value.

@return The default Reference_Resolver String value. @see #Resolver_Default_Value(String) */ public String Resolver_Default_Value () {synchronized (Resolver) {return Resolver.Default_Value ();}} /** Copy a database table.

@param field_names A Vector of field name Strings in the same order as the table record entries. @param table A Vector of records; each record is a Vector of field value Strings any of which may be null. @return A Vector containing a copy of the field_names as its first entry followed by a copy of each record in the table. */ private static Vector> Table ( Vector field_names, Vector> table ) { if (field_names == null || table == null) return null; int row = 0, rows = table.size (); Vector> copy = new Vector> (rows + 1); copy.add (Record (field_names)); while (row < rows) copy.add (Record (table.get (row++))); return copy; } /** Copy a database record.

@param record A Vector of field value Strings any of which may be null. @return A Vector containing a copy of the record. */ private static Vector Record ( // Guaranteed to be non-null. Vector record ) { int column = 0, columns = record.size (); Vector copy = new Vector (columns); while (column < columns) copy.add (record.get (column++)); return copy; } /*------------------------------------------------------------------------------ Procedures */ // Management implementation. /** Get the procedures table.

The table is expected to be delivered with the record fields in {@link Conductor#PROCEDURES_FIELD_NAMES} order, and with the records in processing order.

N.B.: The entire contents of the database procedures table is delivered.

@return A table Vector of record Vectors of field Strings. The first record contains the field names. */ public Vector> Procedures () { if (Procedure_Records == null) return null; synchronized (Procedure_Records) {return Table (Procedures_Map.Actual_to_User (Procedures_Map.All_Actual_Fields ()), Procedure_Records);} } /** Get the {@link #Procedures_Table} from the Database.

The entire Procedures_Table is obtained from the Database.

If a {@link Database_Exception#Disconnected () disconnected Database_Exception} is thrown an attempt is made to {@link #Connect_to_Database() reconnect} to the Database.

@return A Vector containing the procedures table. Each entry is a Vector of fields with the first entry being the field names for the records that follow. @throws Database_Exception If the the procedures table could not be obtained. */ protected Vector> Get_Procedures_Table () throws Database_Exception { if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (">>> Conductor.Get_Procedures_Table" + NL +" Procedures_Table: " + Procedures_Table); Vector> table = null; Database_Exception thrown = null; try {table = The_Database.Select (Procedures_Table);} catch (Database_Exception exception) { thrown = exception; if (thrown.Disconnected () && (thrown = Connect_to_Database ()) == null) { try {table = The_Database.Select (Procedures_Table);} catch (Database_Exception except) {thrown = except;} } } if (thrown != null) throw Database_Error ("Unable to load the procedures table: " + Procedures_Table + NL + thrown.getMessage ()); if ((DEBUG & DEBUG_DATABASE) != 0) { for (int index = 0; index < table.size (); index++) System.out.println (table.get (index)); System.out.println ("<<< Get_Procedures_Table"); } return table; } /** Load the Procedure_Records table.

The entire Procedures table is cached. The first, field names, record is removed and used to construct a Field_Map of required field names/indexes to the available field names.

The database table is first loaded into a temporary records list. A temporary Fields_Map is constructed from the first, field names, record which is removed from the table. The map is used to confirm that all the required fields are present. Then the fields of each record are mapped to their expected order. All the records are then sorted on the {@link #SEQUENCE_FIELD} using a {@link String_Vector_Comparator} to do the comparisons.

If the tentative new table content is different than the current Procedure_Records table the latter is set to the former, and the Procedures_Map set to the new map, while synchronized to prevent the Management from obtaining it while it is in an inconsistent state. Then the Configuration is updated: the {@link #TOTAL_PROCEDURE_RECORDS_PARAMETER} is set to the number of table records (not including the field names record, which was removed to contrstuct the Fields_Map), the {@link #PROCEDURE_COUNT_PARAMETER} is reset to zero, the {@link #PROCEDURE_SEQUENCE_PARAMETER} is reset to the empty string, and the {@link #PROCEDURE_COMPLETION_NUMBER_PARAMETER} is reset to {@link #PROCEDURE_SUCCESS}. Finally a {@link #Report_Processing_Event(Processing_Changes) processing event report} is sent with {@link Processing_Changes#Procedures_Changed(boolean) procedures changed} set.

N.B.: The Procedure_Records and the Procedures_Map are not changed, nor a processing event reported, unless a valid table with different contents from the current table is loaded.

@throws Database_Exception If the {@link #Get_Procedures_Table() procedures table} could not be obtained from the Database, it was empty, or any required {@link #PROCEDURES_FIELD_NAMES} are missing. @throws Configuration_Exception If the Configuration could not be updated with the total number of procedures records. @see Fields_Map */ protected void Load_Procedure_Records () throws Database_Exception, Configuration_Exception { if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (">>> Conductor.Load_Procedure_Records" + NL +" Procedures_Table: " + Procedures_Table); // Get the procedures table. Vector> procedure_records = Get_Procedures_Table (); if (procedure_records.size () < 2) // No procedures. throw Database_Error ("An empty " + Procedures_Table + " procedures table was loaded."); // Construct the map of field names/indexes to field numbers. Fields_Map procedures_map = new Fields_Map (PROCEDURES_FIELD_NAMES, procedure_records.remove (0)); // Confirm that all the fields (except DESCRIPTION_FIELD) are present. String[] fields = (String[])PROCEDURES_FIELD_NAMES.clone (); fields[DESCRIPTION_FIELD] = null; try {procedures_map.Confirm_Fields (fields);} catch (Database_Exception exception) { synchronized (Procedure_Records) { Procedure_Records = procedure_records; Procedures_Map = procedures_map; } throw Database_Error (exception.getMessage () + NL + "in the " + Procedures_Table + " table."); } // Map all the records so their fields are in the expected order. for (int index = 0; index < procedure_records.size (); index++) procedure_records.set (index, procedures_map.Actual_to_User (procedure_records.get (index))); // Sort the procedure records by sequence number. Collections.sort (procedure_records, new String_Vector_Comparator (SEQUENCE_FIELD)); if (! Procedure_Records.equals (procedure_records)) { // Update to the new procedures. synchronized (Procedure_Records) { Procedure_Records = procedure_records; Procedures_Map = procedures_map; } // Update the Configuration. Config_Value (TOTAL_PROCEDURE_RECORDS_PARAMETER, new Integer (Procedure_Records.size ())); Config_Value (PROCEDURE_COUNT_PARAMETER, new Integer (0)); Config_Value (PROCEDURE_SEQUENCE_PARAMETER, ""); Config_Value (PROCEDURE_COMPLETION_NUMBER_PARAMETER, new Integer (PROCEDURE_SUCCESS)); // Report the change. Report_Processing_Event (new Processing_Changes () .Procedures_Changed (true)); if ((DEBUG & DEBUG_DATABASE) != 0) { System.out.println (" " + (Procedure_Records.size ()) + " Procedure_Records -"); for (int index = 0; index < Procedure_Records.size (); index++) System.out.println (Procedure_Records.get (index)); } } else if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (" Procedure_Records unchanged"); if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Load_Procedure_Records"); } private String Procedure_Field ( int field ) {return Procedure_Record.get (field);} private void Procedure_Field ( int field, String value ) {Procedure_Record.set (field, value);} /*------------------------------------------------------------------------------ Sources */ // Management implementation. /** Get the current cache of source records.

The table will be delivered with the record fields in {@link Conductor#SOURCES_FIELD_NAMES} order, and with the records sorted by increasing {@link Conductor#SEQUENCE_FIELD} order.

N.B.: Only the contents of the Conductor source records cache is delivered. The contents of the database sources table may be much, much larger.

@return A table Vector of record Vectors of field Strings. The first record contains the field names. */ public Vector> Sources () { if (Source_Records == null) return null; synchronized (Source_Records) {return Table (Sources_Map.Actual_to_User (Sources_Map.All_Actual_Fields ()), Source_Records);} } /** Get the {@link #Sources_Table} from the Database.

Only those Sources_Table records with a NULL {@link #CONDUCTOR_ID_PARAMETER} field value are selected. Up to Max_Source_Records are obtained.

If a {@link Database_Exception#Disconnected () disconnected Database_Exception} is thrown an attempt is made to {@link #Connect_to_Database() reconnect} to the Database.

@return A Vector containing the selected sources table records. Each entry is a Vector of fields with the first entry being the field names for the records that follow. @throws Database_Exception If the the sources table records could not be obtained. */ private Vector> Get_Sources_Table () throws Database_Exception { if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (">>> Conductor.Get_Sources_Table"); Vector> table = null; Vector table_name = new Vector (1); table_name.add (Sources_Table); Database_Exception thrown = null; try {table = The_Database.Select (table_name, null, SOURCES_FIELD_NAMES[CONDUCTOR_ID_FIELD] + " IS NULL", Max_Source_Records);} catch (Database_Exception exception) { thrown = exception; if (thrown.Disconnected () && (thrown = Connect_to_Database ()) == null) { try {table = The_Database.Select (table_name, null, SOURCES_FIELD_NAMES[CONDUCTOR_ID_FIELD] + " IS NULL", Max_Source_Records);} catch (Database_Exception except) {thrown = except;} } } if (thrown != null) throw Database_Error ("Unable to select from the sources table: " + Sources_Table + NL + thrown.getMessage ()); if ((DEBUG & DEBUG_DATABASE) != 0) { for (int index = 0; index < table.size (); index++) System.out.println (table.get (index)); System.out.println ("<<< Get_Sources_Table"); } return table; } /** Load the Sources_Records table.

If the source records cache is not empty nothing is done.

Records in the Sources table for which the Processing_Host field is NULL are cached. The maximum size of the cache is determined by the {@link #Max_Source_Records} value, which can not be less than the {@link #Min_Source_Records} value.

The first, field names, record from the Database is removed and used to construct the Sources_Map Field_Map of required field names/indexes to the available field names.

@return true if additional source records were loaded; false if no unprocessed source records are available. @throws Database_Exception If the {@link #Get_Sources_Table() sources table records} could not be obtained from the Database or any required {@link #SOURCES_FIELD_NAMES} are missing. @see Fields_Map */ protected boolean Load_Source_Records () throws Database_Exception { if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (">>> Conductor.Load_Source_Records"); if (Source_Records.isEmpty ()) { synchronized (Source_Records) { Source_Records = Get_Sources_Table (); // Construct the map of field names/indexes to field numbers. Sources_Map = new Fields_Map (SOURCES_FIELD_NAMES, Source_Records.remove (0)); // Confirm that all the fields are present. try {Sources_Map.Confirm_Fields (SOURCES_FIELD_NAMES);} catch (Database_Exception exception) { throw Database_Error (exception.getMessage () + NL + "in the " + Sources_Table + " table."); } if (! Source_Records.isEmpty ()) { // Map all the records so their fields are in the expected order. for (int index = 0; index < Source_Records.size (); index++) Source_Records.set (index, Sources_Map.Actual_to_User (Source_Records.get (index))); } } // Report the change. Report_Processing_Event (new Processing_Changes () .Sources_Refreshed (true)); } if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Load_Source_Records: " + (! Source_Records.isEmpty ())); return ! Source_Records.isEmpty (); } /** Acquire an exclusive lock on a source record from the Sources table.

While the Source_Records cache is not empty and no record has been acquired:

The record at the front of the cache is removed and becomes the tentative source record. N.B.: This has the intended effect of consuming the cache.

An attempt is made to mark the tentative source record in the Sources table as unavailable for processing. This must be done in the context of multiple processes contending for the same record at the same time.

The key, literally, of this operation is the assumption that the SQL UPDATE operation will be atomic on the database server. Once the operation is started by the database server it will go to completion without the possibility of interruption by any other database operation. This guarantees that only one process will be able to gain access to any source record. It will be safe to process the source without concern that some other process may interfere.

An UPDATE operation on the unique Sources table record having the target SOURCE_NUMBER_FIELD is done to set the CONDUCTOR_ID_FIELD field to the Conductor_ID, with the condition that its CONDUCTOR_ID_FIELD field is currently NULL. If the UPDATE returns 0, then the field value was not set (it was not NULL) and so we did not acquire an exclusive lock on the record (some other process has claimed it). If it returns 1, then the field value was changed from NULL to our Conductor_ID and so we have acquired an exclusive lock on the record.

When an exclusive lock has been acquired on a tentative source record it is set as the current Source_Record. Then the Configuration {@link #SOURCE_NUMBER_PARAMETER}, {@link #SOURCE_PATHNAME_PARAMETER}, {@link #SOURCE_ID_PARAMETER} and {@link #LOG_PATHNAME_PARAMETER} are set from the corresponding fields of the record; the other source record parameters - {@link #SOURCE_DIRECTORY_PARAMETER}, {@link #SOURCE_FILENAME_PARAMETER}, {@link #SOURCE_FILENAME_ROOT_PARAMETER}, {@link #SOURCE_FILENAME_EXTENSION_PARAMETER}, {@link #LOG_DIRECTORY_PARAMETER}, and {@link #LOG_FILENAME_PARAMETER} - are reset to the empty string (they will be filled in during {@link #Process_Source() source processing}).

@return true if an exclusive lock on the record was acquired; false otherwise. @throws Database_Exception if there was a problem accessing the Database. @throws Configuration_Exception if there was a problem setting the Configuration parameters. */ private boolean Acquire_Source_Record () throws Database_Exception, Configuration_Exception { if ((DEBUG & DEBUG_GET_SOURCE) != 0) System.out.println (">>> Conductor.Acquire_Source_Record: " + Source_Records.size () + " unprocessed records."); Vector record = null; boolean acquired = false; while (! acquired) { if (Source_Records.isEmpty ()) { if ((DEBUG & DEBUG_GET_SOURCE) != 0) System.out.println (" Source_Records cache is empty"); break; } // Pull the next record from the front of the list. record = Source_Records.remove (0); if ((DEBUG & DEBUG_GET_SOURCE) != 0) System.out.println (" Source_Number: " + Sources_Map.entry (record, SOURCE_NUMBER_FIELD) + NL +" Source_ID: " + Sources_Map.entry (record, SOURCE_ID_FIELD) + NL +" Source_Pathname: " + Sources_Map.entry (record, SOURCE_PATHNAME_FIELD)); /* Assemble the database record acquisition operation. This MUST be an atomic database operation that acquires the record with an exclusive lock, vis a vis other Conductor process contending for the same record. */ String SQL_update = "UPDATE " + Sources_Table +" SET " + SOURCES_FIELD_NAMES[CONDUCTOR_ID_FIELD] + "='" + Conductor_ID + "'" +" WHERE " + SOURCES_FIELD_NAMES[SOURCE_NUMBER_FIELD] + "=" + Sources_Map.entry (record, SOURCE_NUMBER_FIELD) + " AND " + SOURCES_FIELD_NAMES[CONDUCTOR_ID_FIELD] + " is NULL"; if ((DEBUG & DEBUG_GET_SOURCE) != 0) System.out.println (" SQL Update -" + NL + SQL_update); // Send the SQL to the database server. Database_Exception thrown = null; try {acquired = (The_Database.Update (SQL_update) == 1);} catch (Database_Exception exception) { thrown = exception; if (thrown.Disconnected () && (thrown = Connect_to_Database ()) == null) { try {acquired = (The_Database.Update (SQL_update) == 1);} catch (Database_Exception except) {thrown = except;} } } if (thrown != null) throw Database_Error ("Unable to acquire a lock on source record " + Sources_Map.entry (record, SOURCE_NUMBER_FIELD) + '.' + NL + thrown.getMessage ()); } if (acquired) { Source_Record = record; Source_Field (CONDUCTOR_ID_FIELD, Conductor_ID); String value; if ((value = Source_Record.get (SOURCE_NUMBER_FIELD)) == null) value = ""; Config_Value (SOURCE_NUMBER_PARAMETER, value); if ((value = Source_Record.get (SOURCE_ID_FIELD)) == null) value = ""; Config_Value (SOURCE_ID_PARAMETER, value); if ((value = Source_Record.get (SOURCE_PATHNAME_FIELD)) == null) value = ""; Config_Value (SOURCE_PATHNAME_PARAMETER, value); if ((value = Source_Record.get (LOG_PATHNAME_FIELD)) == null) value = ""; Config_Value (LOG_PATHNAME_PARAMETER, value); Config_Value (SOURCE_DIRECTORY_PARAMETER, ""); Config_Value (SOURCE_FILENAME_PARAMETER, ""); Config_Value (SOURCE_FILENAME_ROOT_PARAMETER, ""); Config_Value (SOURCE_FILENAME_EXTENSION_PARAMETER, ""); Config_Value (LOG_DIRECTORY_PARAMETER, ""); Config_Value (LOG_FILENAME_PARAMETER, ""); } if ((DEBUG & DEBUG_GET_SOURCE) != 0) System.out.println ("<<< Acquire_Source_Record: " + acquired); return acquired; } private String Source_Field ( int field ) {return Source_Record.get (field);} private void Source_Field ( int field, String value ) {Source_Record.set (field, value);} private void Update_Source_Record ( int field, String value ) throws Database_Exception { if ((DEBUG & DEBUG_TABLE_UPDATE) != 0) System.out.println (">>> Conductor.Update_Source_Record: field " + field); if (field < 0 || field >= SOURCES_FIELD_NAMES.length) throw Database_Error ("Can't update Sources field number " + field + "." + NL +"No such field: Programming bug in the use of Update_Source_Record!", Source_Field (SOURCE_NUMBER_FIELD)); if ((DEBUG & DEBUG_TABLE_UPDATE) != 0) System.out.println (" " + SOURCES_FIELD_NAMES[field] + " = " + value); // Assemble the SQL statement. String SQL_update = "UPDATE " + Sources_Table + " SET " + SOURCES_FIELD_NAMES[field] + "='" + value + "'" + " WHERE " + SOURCES_FIELD_NAMES[SOURCE_NUMBER_FIELD] + "=" + Source_Field (SOURCE_NUMBER_FIELD); if ((DEBUG & DEBUG_TABLE_UPDATE) != 0) System.out.println (" SQL Update -" + NL + SQL_update); // Send the SQL to the database server. Database_Exception thrown = null; int count = 0; try {count = The_Database.Update (SQL_update);} catch (Database_Exception exception) { thrown = exception; if (thrown.Disconnected () && (thrown = Connect_to_Database ()) == null) { try {count = The_Database.Update (SQL_update);} catch (Database_Exception except) {thrown = except;} } } if (thrown != null) throw Database_Error ("Update failed -" + NL + thrown.getMessage ()); if (count == 0) throw Database_Error ("Update failed -" + NL + SQL_update); // Update the Source_Record. Source_Field (field, value); // Report the change. Report_Processing_Event (new Processing_Changes () .Source_Record (Source_Record)); if ((DEBUG & DEBUG_TABLE_UPDATE) != 0) System.out.println ("<<< Update_Source_Record"); } /*============================================================================== Pipeline Processing */ // Management implementation. /** Get the current Conductor processing state.

The possible processing state values are:

{@link Conductor#RUNNING}
Source records are being processing.
{@link Conductor#POLLING}
No unprocessed source records are currently available for processing; the Conductor is {@link #Poll_Interval(int) polling} for source records to process.
{@link Conductor#RUN_TO_WAIT}
When processing of the current source record completes Conductor will go into the waiting state.
{@link Conductor#WAITING}
The Conductor is waiting to be told to being processing.
{@link Conductor#HALTED}
A problem condition caused the Conductor to halt processing. The problem may be the result of the maximum number of {@link #Stop_on_Failure(int) sequential failures} of source record processing having occured, a database access failure, or some other {@link #Processing_Exception system error}.

The WAITING and HALTED state codes are negative; all others are positive.

@return A Conductor processing state code. */ public int Processing_State () {return Processing_State;} // Management implementation. /** Get the current Conductor processing conditions state.

All Processing_Changes state variables will be set to the current values from this Conductor, except the flag variables - {@link Processing_Changes#Sources_Refreshed(boolean)}, {@link Processing_Changes#Procedures_Changed(boolean)} and {@link Processing_Changes#Exiting(boolean)} - will always be false.

@return A Processing_Changes object containing the values of the current Conductor processing state variables. */ public Processing_Changes State () { if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) System.out.println (">>> Conductor.State"); Processing_Changes state = null; try { state = new Processing_Changes () .Configuration (The_Configuration) .Processing_State (Processing_State) .Source_Record (Source_Record) .Procedure_Record (Procedure_Record) .Sequential_Failures (Sequential_Failures) .Error_Condition ((Processing_Exception == null) ? null : Processing_Exception.toString ()); } catch (Configuration_Exception exception) {/* Shouldn't happen with a good configuration. */} if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) System.out.println (state + NL +"<<< Conductor.State"); return state; } // Management implementation. /** Start pipeline processing.

If a pipeline processing thread is not running one is started. Otherwise, if the {@link #RUN_TO_WAIT} state is in effect it is reset to the {@link #RUNNING} state.

Pipeline Processing:

The Conductor reconfigures itself by re-reading its Configuration source in case it was changed while processing was waiting. N.B.: The {@link #Poll_Interval(int) poll interval} and {@link #Stop_on_Failure(int) stop-on-failure limit} are not reset if they were set to a non-negative value.

The {@link #Load_Procedure_Records() table of procedures} is refreshed in case it was changed while processing was waiting.

Source procesing will continue indefinately unless: the {@link #Poll_Interval() polling interval} for newly available sources is zero (i.e. batch mode) and at least Min_Source_Records have been processed in the current batch; processing of the current source has completed and further processing has been flagged to {@link #Stop() stop}; the number of sequential procedure failures has reached its (@link #Stop_on_Failure() limit}, or an unrecoverable exception occurred during processing, in which case the {@link #Processing_Exception() processing exception} will be non-null. The possible processing exception types are a Configuration_Exception, Database_Exception or IOException; any other exception is due to a programming error (any exception is caught and saved for possible subsequent retrieval);

The current contents of the {@link #Load_Source_Records() source records cache} is processed first. An unprocessed source record is {@link #Acquire_Source_Record() acquired} from the cache and {@link #Process_Source() processed}. Source record acquisition is always done in the order in which source records occur in the list of records obtained from the database; there is no priority ordering. Only after the cache is emtpy and the polling interval {@link #Sleep(long) sleep time} has passed will it be refreshed. If there is no polling interval the cache will not be refreshed and processing will stop. The polling interval may be interrupted by stopping and {@link #Start() restarting} processing. */ public void Start () { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Conductor.Start" + NL +" Processor == null? " + (Processor == null)); if (Processor == null) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" The Processor will be started"); Processor = new Thread (new Pipeline_Processor ()); Processor.start (); } else if (Processing_State == RUN_TO_WAIT) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Processing_State changing from RUN_TO_WAIT to RUNNING"); Report_Processing_Event (new Processing_Changes () .Processing_State (Processing_State = RUNNING)); } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println ("<<< Start"); } private class Pipeline_Processor implements Runnable { public void run () { Process_Pipeline (); Processor = null; } } // End of Pipeline_Processor class. /** Pipeline processing.

@see #Start() */ private void Process_Pipeline () { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (">>> Conductor.Process_Pipeline: " + Pipeline); Processing_Exception = null; Processing_Changes processing_changes = null; Report_Processing_Event (new Processing_Changes () .Processing_State (Processing_State = RUNNING)); try { Reconfigure (); Load_Procedure_Records (); Load_Source_Records (); Batch_Sources_Count = 0; while (Processing_State > RUN_TO_WAIT) { if (Acquire_Source_Record ()) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Acquire_Source_Record true"); if (Processing_State == POLLING) Report_Processing_Event (new Processing_Changes () .Processing_State (Processing_State = RUNNING)); Process_Source (); } else { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Acquire_Source_Record false" + NL +" Poll_Interval = " + Poll_Interval + NL +" Source_Success_Count = " + Source_Success_Count + NL +" Total_Failure_Count = " + Total_Failure_Count); int poll_interval = Poll_Interval; if (poll_interval <= 0) { // Batch mode. if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Batch mode -" + NL +" Poll_Interval = " + Poll_Interval + NL +" BATCH_POLL_INTERVAL = " + BATCH_POLL_INTERVAL + NL +" Batch_Sources_Count = " + Batch_Sources_Count + NL +" Min_Source_Records = " + Min_Source_Records); poll_interval = BATCH_POLL_INTERVAL; if (Batch_Sources_Count >= Min_Source_Records) { // Batch mode completed. if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Batch mode completed"); break; } } if (! Load_Source_Records ()) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Load_Source_Records false" + NL +" Poll_Interval = " + Poll_Interval + NL +" Processing_State = " + Processing_State); if (Poll_Interval > 0 && Processing_State > RUN_TO_WAIT) { if (Processing_State != POLLING) Report_Processing_Event (new Processing_Changes () .Processing_State (Processing_State = POLLING)); if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Sleep for " + Poll_Interval + "seconds"); Sleep (poll_interval); } else break; } } } } catch (Exception exception) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println ("==> Process_Pipeline: Exception - " + NL + exception); Processing_State = HALTED; Processing_Exception = exception; try {processing_changes = new Processing_Changes () .Configuration (The_Configuration);} catch (Configuration_Exception except) {} processing_changes .Sequential_Failures (Sequential_Failures) .Error_Condition (Processing_Exception.toString ()); } if (Processing_State != HALTED) Processing_State = WAITING; if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println ("==> Process_Pipeline: State " + Processing_State); if (processing_changes == null) processing_changes = new Processing_Changes (); Report_Processing_Event (processing_changes .Processing_State (Processing_State)); if (Processing_State == HALTED) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Processing HALTED"); Send_Failure_Notification (new Status_Report (this, null, Processing_Exception)); } if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Main_Thread_Waiting != null? " + (Main_Thread_Waiting != null) + NL +" Connected_to_Stage_Manager? " + Connected_to_Stage_Manager () + NL +" Poll_Interval = " + Poll_Interval + NL +" Theater_Management.Auto_Open? " + Theater_Management.Auto_Open ()); if (Main_Thread_Waiting != null && // No local Management. ! Connected_to_Stage_Manager () && // No remote Management. (Poll_Interval <= 0 || // Batch mode. ! Theater_Management.Auto_Open ())) // Not expecting remote Management. { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (" Stopping main thread"); Main_Thread_Waiting.interrupt (); // Stop the main thread. } Processor = null; } /** Puts the Conductor processing to sleep for some time.

A lock is acquired on the Conductor object and it is put in a wait state for the specified amount of time. This blocks all further processing (and should not consume CPU resources) until the sleep period has expired, at which time the method will return.

Note: The process will awake earlier than the sleep time if the wait is externally interrupted.

@param seconds The number of seconds to sleep. */ private void Sleep ( long milliseconds ) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println (">-< Conductor.Sleeping for " + ((double)milliseconds / 1000.0) + " seconds ..."); if (milliseconds > 0) { try { Sleeping = true; Processor.sleep (milliseconds); } catch (InterruptedException exception) { if ((DEBUG & DEBUG_PIPELINE) != 0) System.out.println ("Woke early!"); } } Sleeping = false; } private void Sleep ( int seconds ) {Sleep ((long)(seconds * 1000));} // Management implementation. /** Set the time interval to poll for unprocessed source records.

The new value will override the value set when the Conductor is {@link #Postconfigure(Configuration) reconfigured} after being stopped. However, if the value is negative then the value from the reconfiguration will be used. In this case a zero value will still be set for the current poll interval; this will cause the Conductor to stop if it is currently polling or when no unprocessed source records can be obtained from the database.

@param seconds The number of seconds between querying the database for unprocessed source records. If negative zero will be used. @return This Management interface. */ public Management Poll_Interval ( int seconds ) { if (seconds < 0) { seconds = 0; Poll_Interval_Override = UNSET_INTEGER_VALUE; } else Poll_Interval_Override = seconds; if (seconds != Poll_Interval) { try { Config_Value (POLL_INTERVAL_PARAMETER, new Integer (Poll_Interval = seconds)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration)); } catch (Configuration_Exception exception) {/* Previously set */} if (Poll_Interval <= 0 && Processing_State == POLLING) Stop (); } return this; } /** Get the interval at which the Conductor will poll for unprocessed source records.

@return The interval, in seconds, at which the Conductor will poll for unprocessed source records. If zero, polling has been disabled. @see #Poll_Interval(int) */ public int Poll_Interval () {return Poll_Interval;} // Management implementation. /** Set the sequential failure limit at which to halt processing source records.

The new value will override the value set when the Conductor is {@link #Postconfigure(Configuration) reconfigured} after being stopped. However, if the value is negative then the value from the reconfiguration will be used. In this case the current value will not be changed.

@param failure_count The number of sequential failures at which to halt processing. Zero means never halt. If negative the current value is not changed. @return This Management interface. */ public Management Stop_on_Failure ( int failure_count ) { if (failure_count < 0) Stop_on_Failure_Override = UNSET_INTEGER_VALUE; else { Stop_on_Failure_Override = failure_count; if (failure_count != Stop_on_Failure) { try { Config_Value (STOP_ON_FAILURE_PARAMETER, new Integer (Stop_on_Failure = failure_count)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration)); } catch (Configuration_Exception exception) {/* Previously set */} } } return this; } // Management implementation. /** Get the number of Conductor sequential source processing failures at which to stop processing.

@return The number of Conductor sequential source processing failures at which to stop processing. If zero sequential processing failures will never cause processing to stop. @see #Stop_on_Failure(int) */ public int Stop_on_Failure () {return Stop_on_Failure;} // Management implementation. /** Get the count of sequential source processing failures that the Conductor has accumulated.

N.B.: A processing event notification is sent to all listeners

@return The count of sequential source processing failures that the Conductor has accumulated. @see #Stop_on_Failure(int) @see #Reset_Sequential_Failures() */ public int Sequential_Failures () {return Sequential_Failures;} // Management implementation. /** Reset the count of sequential source processing failures that the Conductor has accumulated.

If the {@link #Processing_State() processing state} is {@link #HALTED} it is reset to {@link #WAITING}.

If the count of sequential source processing failures and/or the processing state was changed a processing event notification with these changes is sent to all listeners.

@return This Conductor Management interface.. */ public Management Reset_Sequential_Failures () { Processing_Changes changes = null; if (Sequential_Failures != 0) changes = new Processing_Changes () .Sequential_Failures (Sequential_Failures = 0); if (Processing_State == HALTED) { if (changes == null) changes = new Processing_Changes (); changes .Processing_State (Processing_State = WAITING); } Report_Processing_Event (changes); return this; } // Management implementation. /** Get the exception that caused Conductor processing to halt.

N.B.: When Conductor processing is {@link #Start() started} the previous processing exception is cleared.

@return The Exception that caused processing to halt. This will be null if processing did not halt as the result of an exception, or the current processing state is not halted. */ public Exception Processing_Exception () {return Processing_Exception;} // Management implementation. /** Stop pipeline processing after the current source processing has completed.

If the Conductor is in a positive {@link #Processing_State() processing state} it will enter the {@link Conductor#RUN_TO_WAIT run-to-wait} state in which will enter the {@link Conductor#WAITING waiting} state when the current source record completes processing. If the Conductor is in the {@link Conductor#POLLING polling} state it will immediately stop polling for new source records. There will be no effect for any negative state. */ public void Stop () { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Conductor.Stop"); Thread processor = Processor; if (processor != null) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" The Processor is running; RUN_TO_WAIT state set"); if (Processing_State > RUN_TO_WAIT) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Setting RUN_TO_WAIT state"); Report_Processing_Event (new Processing_Changes () .Processing_State (Processing_State = RUN_TO_WAIT)); } if (Sleeping) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Interrupting the sleeping Processor"); synchronized (Logger) { try {processor.interrupt ();} catch (SecurityException exception) {} } } } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println ("<<< Stop"); } // Management implementation. /** Immediately stop processing and exit.

Any open log file is closed. The database server is disconnected. An {@link Processing_Changes#Exiting(boolean) exiting} processing event is sent to all {@link #Add_Processing_Listener(Processing_Listener) processing listeners}. The application exits with a {@link #EXIT_SUCCESS success status}.

N.B.: If source processing is {@link #Processing_State() running} it is aborted. */ public void Quit () { if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (">-< Conductor.Quit"); Exit (new Status_Report (this)); } /*------------------------------------------------------------------------------ Processing Events */ // Management implementation. /** Register a processing state change listener.

The Conductor sends its {@link Processing_Event processing event} notifications to all registered listeners.

@param listener A Processing_Listener. @return This Conductor Management object. @see Processing_Listener */ public Management Add_Processing_Listener ( Processing_Listener listener ) { if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) System.out.println (">>> Conductor.Add_Processing_Listener -" + NL + listener); boolean report = false; synchronized (Processing_Listeners) { if (listener != null && ! Processing_Listeners.contains (listener)) report = Processing_Listeners.add (listener); } if (report) { // Report all processing states to the new listener. listener.Processing_Event_Occurred (new Processing_Event (this, State ())); /* if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) { synchronized (this) { try {wait (5000);} catch (InterruptedException exception) {} } } */ } if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) System.out.println ("<<< Add_Processing_Listener"); return this; } // Management implementation. /** Unregister a processing state change listener.

@param listener The Processing_Listener to be removed from the Management list of registered listeners. @return true If the listener was registered and is now removed; false if it was not registered. @see #Add_Processing_Listener(Processing_Listener) */ public boolean Remove_Processing_Listener ( Processing_Listener listener ) { synchronized (Processing_Listeners) {return Processing_Listeners.remove (listener);} } private void Report_Processing_Event ( Processing_Changes changes ) { if (changes == null) return; if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) System.out.println (">>> Conductor.Report_Processing_Event"); synchronized (Processing_Listeners) { int index = Processing_Listeners.size (); if (index != 0) { Processing_Event event = new Processing_Event (this, changes); while (--index >= 0) { try {Processing_Listeners.get (index) .Processing_Event_Occurred (event);} catch (Exception exception) {/* Ignore all exceptions. */} } } } if ((DEBUG & DEBUG_PROCESSING_EVENT) != 0) { /* synchronized (this) { try {wait (5000);} catch (InterruptedException exception) {} } */ System.out.println ("<<< Report_Processing_Event"); } } /*------------------------------------------------------------------------------ Source Record Processing */ /** Process the current Source_Record. */ private void Process_Source () throws Configuration_Exception, Database_Exception, IOException { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (">>> Conductor.Process_Source"); ++Batch_Sources_Count; String error_report = null; Source_Status = Status_Indicators (Source_Field (STATUS_FIELD)); Config_Value (PROCEDURE_COUNT_PARAMETER, new Integer (Source_Status.size ())); Procedure_has_Status = false; /* Ensure that the File is constructed with an absolute pathname. This is necessary to be able to obtain the File's directory path (getParent) which will be null if the pathname String used to construct the File is relative. */ File source_file = new File (new File (Source_Field (SOURCE_PATHNAME_FIELD)).getAbsolutePath ()); String source_number = Source_Field (SOURCE_NUMBER_FIELD), source_id = Source_Field (SOURCE_ID_FIELD), source_log_pathname = Source_Field (LOG_PATHNAME_FIELD), log_pathname = source_log_pathname; // Source ID. if (source_id == null || source_id.length () == 0) { // Use the filename portion of the source pathname. source_id = source_file.getName (); int index = source_id.lastIndexOf ('.'); if (index >= 0) // Remove the extension. source_id = source_id.substring (0, index); // Add the source ID to the source record. Update_Source_Record (SOURCE_ID_FIELD, source_id); } else Report_Processing_Event (new Processing_Changes () .Source_Record (Source_Record)); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Source_Number: " + source_number + NL +" Source_ID: " + source_id + NL +" Source_Pathname: " + source_file.getAbsolutePath ()); // Ensure that the previous log file is closed. Close_Log_File (); /*.............................................................................. Open a log file. If the LOG_PATHNAME_FIELD is empty (or null) the LOG_DIRECTORY_PARAMETER (or LOG_PATHNAME_PARAMETER) from Log_File_Directory will be used. If both are empty the default filename will be used with no directory (i.e. relative to the CWD). If either are not empty the resulting pathname string is reference resolved. If the resolved pathname string refers to an existing directory the FILE_SEPARATOR and default filename will be appended to the pathname. The default log filename is: [/]-_.log The Pipeline name includes the leading database catalog name separated by a period ('.') character. The Source_ID and the Source_Number are obtained from the current source record. Note, however, that there is a chance that the Source_ID will include characters that are unsafe for use as part of a file name. For now, we'll assume that the only unsafe character is the system's FILE_SEPARATOR character ('/' for Unix) which will be replaced with the FILE_SEPARATOR_SUBSTITUTE (probably '%') character. Another option would be to URL-encode the Source_ID, but this results in an ugly filename. Note: If either the LOG_PATHNAME_FIELD or the Log_File_Directory value is not empty and does not refer to a directory logging output will be appended to that file (it will be created if it does not exist). */ boolean append = false; if (log_pathname == null || log_pathname.length () == 0) // Use the Log_File_Directory by default (may actually be a file pathname). log_pathname = Log_File_Directory; if (log_pathname == null || log_pathname.length () == 0) // Use the derived filename in the CWD when no default Log_File_Directory. log_pathname = Pipeline + '-' + source_id.replace (FILE_SEPARATOR, FILE_SEPARATOR_SUBSTITUTE) + '_' + source_number + ".log"; else { // Resolve the pathname, whether from source record or Log_File_Directory. try {log_pathname = Resolve (log_pathname);} catch (Exception exception) { error_report = "Unable to resolve reference in parameter \"" + LOG_DIRECTORY_PARAMETER + "\"" + NL + exception.getMessage (); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; throw Database_Error (error_report, source_number); } if (new File (log_pathname).isDirectory ()) log_pathname += FILE_SEPARATOR + Pipeline + '-' + source_id.replace (FILE_SEPARATOR, FILE_SEPARATOR_SUBSTITUTE) + '_' + source_number + ".log"; else // Pathname is to file; append to it (if it exists). append = true; } if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Log file pathname: " + log_pathname + (append ? "(append)" : "")); File log_file = new File (new File (log_pathname).getAbsolutePath ()); // Open the new log file. try {Log_File_Writer = new FileWriter (log_file, append);} catch (IOException exception) { error_report = Error_Message ("Unable to open log file: " + log_file.getAbsolutePath ()) + NL + exception.getMessage (); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; throw new IOException (error_report); } // Add the log file writer to the Logger. Add_Log_Writer (Log_File_Writer); if (! log_pathname.equals (source_log_pathname)) // Update the log file pathname in the source record. Update_Source_Record (LOG_PATHNAME_FIELD, log_file.getAbsolutePath ()); // Write the source identification into the log file. Log_Message (ID + NL); Log_Message (SOURCE_FILE_LOG_DELIMITER, MARKER_STYLE); Log_Message (new Date () + NL + NL); Log_Message ("Configuration: " + Config_Value (CONFIGURATION_SOURCE_PARAMETER) + NL +"Database: " + Config_Value (DATABASE_TYPE_PARAMETER) +" server on host " + Config_Value (DATABASE_HOSTNAME_PARAMETER) + NL +"Source: " + Sources_Table + NL + SOURCES_FIELD_NAMES[SOURCE_NUMBER_FIELD] + ": " + source_number + NL + SOURCES_FIELD_NAMES[SOURCE_ID_FIELD] + ": " + source_id + NL + SOURCES_FIELD_NAMES[SOURCE_PATHNAME_FIELD] + ": " + source_file.getAbsolutePath () + NL + NL, HIGHLIGHT_STYLE); /*.............................................................................. Status indicators check. The Source_Status Vector is paired with the Procedure_Records Vector. Each procedure that is processed gets a status indicator in the Source_Status Vector. The Procedure_Records are processed in the order they have been sorted. */ if (! Source_Status.isEmpty ()) { // Check the last procedure for successful completion. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Initial status: " + Source_Status); Log_Message ("The source appears to have been previously processed" + NL +"producing status " + Status_Field_Value (Source_Status) + NL + NL, NOTICE_STYLE); int conductor_code = UNSET_INTEGER_VALUE; try {conductor_code = Status_Conductor_Code (Source_Status.lastElement ());} catch (NumberFormatException exception) {} if (conductor_code != PROCEDURE_SUCCESS) { error_report = "Previous failure condition: " + Status_Conductor_Code_Description (conductor_code) + '.' + NL +"Processing skipped for this source."; Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration) .Sequential_Failures (++Sequential_Failures)); if (Stop_on_Failure != 0 && Sequential_Failures >= Stop_on_Failure) Processing_State = HALTED; Close_Log_File (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println ("<<< Process_Source (Prior failure condition)"); return; } } if (Source_Available_Tries == SOURCE_AVAILABLE_NO_CHECK) Log_Message ("Source file availability check disabled." + NL + NL, HIGHLIGHT_STYLE); else { /*.......................................................................... Confirm access to a readable source file. */ /* Due to NFS filesystems latency it is possible that a source file that has just been registered will not appear if accessed too soon. So this hack provides a ten second delay on failure between three tries to access the file. */ for (int tries = Source_Available_Tries; tries > 0 && ! source_file.canRead (); tries--) { if (Source_Available_Tries > 1 && Source_Available_Tries == tries) Log_Message ("Can't access file " + source_file.getAbsolutePath () + NL +"Will retry up to " + Source_Available_Tries + " times ..." + NL + NL, NOTICE_STYLE); Sleep (10); if (Processing_State == RUN_TO_WAIT) { Log_Message ("Processing has been interrupted.", NOTICE_STYLE); break; } } if (! source_file.canRead () || ! source_file.isFile ()) { error_report = "Can't access file " + source_file.getAbsolutePath () + NL +"Processing of this file canceled."; Log_Message (error_report + NL + NL, NOTICE_STYLE); Procedure_Status (INACCESSIBLE_FILE); Config_Value (PROCEDURE_COMPLETION_NUMBER_PARAMETER, new Integer (INACCESSIBLE_FILE)); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration) .Sequential_Failures (++Sequential_Failures)); if (Stop_on_Failure != 0 && Sequential_Failures >= Stop_on_Failure) Processing_State = HALTED; Close_Log_File (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println ("<<< Process_Source (No Source File!)"); return; } } /*.............................................................................. Configuration parameters update. */ Config_Value (SOURCE_NUMBER_PARAMETER, source_number); Config_Value (SOURCE_ID_PARAMETER, source_id); Config_Value (SOURCE_PATHNAME_PARAMETER, source_file.getAbsolutePath ()); Config_Value (SOURCE_DIRECTORY_PARAMETER, source_file.getParent ()); String name = source_file.getName (); Config_Value (SOURCE_FILENAME_PARAMETER, name); int index = name.lastIndexOf ('.'); if (index < 0) index = name.length (); Config_Value (SOURCE_FILENAME_ROOT_PARAMETER, name.substring (0, index)); if (index < name.length ()) index++; Config_Value (SOURCE_FILENAME_EXTENSION_PARAMETER, name.substring (index)); Config_Value (LOG_FILENAME_PARAMETER, log_file.getName ()); Config_Value (LOG_DIRECTORY_PARAMETER, log_file.getParent ()); /*.............................................................................. Process the procedures. */ Log_Message ("Processing host: " + Conductor_ID + NL +"Procedures: " + Procedures_Table + NL + NL, HIGHLIGHT_STYLE); int conductor_status = PROCEDURE_SUCCESS, procedure_status; String procedure_sequence = null; Procedures_Sequence: while (Source_Status.size () < Procedure_Records.size ()) { // Get a copy of the next procedure record for processing. Procedure_Record = new Vector (Procedure_Records.get (Source_Status.size ())); procedure_sequence = Procedure_Field (SEQUENCE_FIELD); Procedure_has_Status = false; if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println ("=== Procedure sequence: " + procedure_sequence); Config_Value (PROCEDURE_COUNT_PARAMETER, new Integer (Source_Status.size () + 1)); Config_Value (PROCEDURE_SEQUENCE_PARAMETER, procedure_sequence); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration) .Procedure_Record (Procedure_Record)); // Get the Command_Line and resolve it. String command_line = Procedure_Field (COMMAND_LINE_FIELD); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" " + PROCEDURES_FIELD_NAMES[COMMAND_LINE_FIELD] + ": " + command_line); if (command_line == null || (command_line = command_line.trim ()).length () == 0) { conductor_status = INVALID_DATABASE_ENTRY; error_report = "Invalid field \"" + PROCEDURES_FIELD_NAMES[COMMAND_LINE_FIELD] + "\"" + NL + " with value: " + ((command_line == null) ? "NULL" : "empty"); } if (error_report == null) { try { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Resolving the command line"); command_line = Resolve (command_line); Procedure_Field (COMMAND_LINE_FIELD, command_line); } catch (Exception exception) { conductor_status = UNRESOLVABLE_REFERENCE; error_report = "Unable to resolve reference in field \"" + PROCEDURES_FIELD_NAMES[COMMAND_LINE_FIELD] + "\"" + NL + exception.getMessage (); } } // Get the Time_Limit and resolve it. String time_limit_string = null; if (error_report == null) { time_limit_string = Procedure_Field (TIME_LIMIT_FIELD); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" " + PROCEDURES_FIELD_NAMES[TIME_LIMIT_FIELD] + ": " + time_limit_string); if (time_limit_string == null || time_limit_string.trim ().length () == 0) time_limit_string = "0"; if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Resolving the time limit"); try {time_limit_string = Resolve (time_limit_string);} catch (Exception exception) { conductor_status = UNRESOLVABLE_REFERENCE; error_report = "Unable to resolve reference in field \"" + PROCEDURES_FIELD_NAMES[TIME_LIMIT_FIELD] + "\"" + NL + exception.getMessage (); } } // Evaluate the Time_Limit value. int time_limit = 0; if (error_report == null) { try { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Expression parsing the time limit"); time_limit = (int)(Expression_Parser.parse (time_limit_string).getVal ()); Procedure_Field (TIME_LIMIT_FIELD, String.valueOf (time_limit)); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Time limit: " + time_limit); } catch (ParseError exception) { conductor_status = INVALID_DATABASE_ENTRY; error_report = "Unable to evaluate expression in field \"" + PROCEDURES_FIELD_NAMES[TIME_LIMIT_FIELD] + "\"" + NL +" with value: " + Resolver.Pattern () + NL +" resolved to: " + time_limit_string + NL +" At index " + exception.context.pos + ((exception.context.tokenString == null) ? "" : (" for token \"" + exception.context.tokenString + '"')) + ":" + NL +" " + exception.getMessage (); } } if (error_report != null) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Procedure_Status (conductor_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); Close_Log_File (); throw Database_Error (error_report, source_number, procedure_sequence); } /*.......................................................................... Run the procedure. */ if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Running the procedure"); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); procedure_status = Run_Procedure (command_line, time_limit, NORMAL); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure status: " + procedure_status); Config_Value (PROCEDURE_COMPLETION_NUMBER_PARAMETER, new Integer (procedure_status)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration)); if (procedure_status < 0) { // The procedure failed to execute correctly. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure failed to execute"); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); Procedure_Status (conductor_status = procedure_status); break Procedures_Sequence; } /*.......................................................................... Check the competion status. The procedure completed and returned an exit status. Determine if the procedure completed successfully or not. */ // Ensure non-null trimmed strings. String success_status_string = Procedure_Field (SUCCESS_STATUS_FIELD); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" " + PROCEDURES_FIELD_NAMES[SUCCESS_STATUS_FIELD] + ": " + success_status_string); if (success_status_string == null) success_status_string = ""; else success_status_string = success_status_string.trim (); String success_message = Procedure_Field (SUCCESS_MESSAGE_FIELD); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" " + PROCEDURES_FIELD_NAMES[SUCCESS_MESSAGE_FIELD] + ": " + success_message); if (success_message == null) success_message = ""; else success_message = success_message.trim (); if (success_status_string.length () == 0 && success_message.length () == 0) { if (Empty_Success_Any) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Empty_Success_Any"); conductor_status = PROCEDURE_SUCCESS; Log_Message ("Procedure completed with implied success." + NL + NL); } else { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Implicit success value 0"); Log_Message (NL + "The " + PROCEDURES_FIELD_NAMES[SUCCESS_STATUS_FIELD] + " and " + PROCEDURES_FIELD_NAMES[SUCCESS_MESSAGE_FIELD] +" fields are both empty." + NL +"Asserting " + PROCEDURES_FIELD_NAMES[SUCCESS_STATUS_FIELD] + " = 0." + NL); if (procedure_status == 0) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure success"); conductor_status = PROCEDURE_SUCCESS; Log_Message ("Procedure completed with success status." + NL + NL); } else { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure failure"); conductor_status = PROCEDURE_FAILURE; Log_Message ("Expected exit status 0." + NL + NL, NOTICE_STYLE); } } } else if (success_status_string.length () != 0) { // Use Success_Status. int success_status_value; String status_string = null; if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Resolving success status"); try {status_string = Resolve (success_status_string);} catch (Exception exception) { error_report = "Unable to resolve reference in field \"" + PROCEDURES_FIELD_NAMES[SUCCESS_STATUS_FIELD] + "\"" + NL + exception.getMessage (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Procedure_Status (UNRESOLVABLE_REFERENCE, procedure_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); Close_Log_File (); throw Database_Error (error_report, source_number, procedure_sequence); } // Attempt expression evaluation. try { // Assume it's a logical expression. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Logical expression parsing the success status"); success_status_value = (int)(Expression_Parser.parseLogical (status_string).getVal ()); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Success status condition: " + success_status_value); Procedure_Field (SUCCESS_STATUS_FIELD, String.valueOf (success_status_value)); if (success_status_value == 0) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure failure"); conductor_status = PROCEDURE_FAILURE; Log_Message ("Procedure completed with success status condition false." + NL +"Conditional: " + success_status_string + NL +"Resolves to: " + status_string + NL + NL, NOTICE_STYLE); } else { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure success"); conductor_status = PROCEDURE_SUCCESS; Log_Message ("Procedure completed with success status condition true." + NL + NL); } } catch (ParseError parse_error) { // Try again as a numeric expression. try { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Expression parsing the success status"); success_status_value = (int)(Expression_Parser.parse (status_string).getVal ()); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Success status value: " + success_status_value); Procedure_Field (SUCCESS_STATUS_FIELD, String.valueOf (success_status_value)); if (procedure_status == success_status_value) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure success"); conductor_status = PROCEDURE_SUCCESS; Log_Message ("Procedure completed with success status." + NL + NL); } else { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure failure"); conductor_status = PROCEDURE_FAILURE; Log_Message ("Expected exit status " + success_status_value + '.' + NL + NL, NOTICE_STYLE); } } catch (ParseError exception) { error_report = "Unable to evaluate expression in field \"" + PROCEDURES_FIELD_NAMES[SUCCESS_STATUS_FIELD] + "\"" + NL +" with value: " + Resolver.Pattern () + NL +" resolved to: " + status_string + NL +" At index " + exception.context.pos + ((exception.context.tokenString == null) ? "" : (" for token \"" + exception.context.tokenString + '"')) + ":" + NL +" " + exception.getMessage (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Procedure_Status (INVALID_DATABASE_ENTRY, procedure_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); Close_Log_File (); throw Database_Error (error_report, source_number, procedure_sequence); } } } else { // Use Success_Message. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Resolving the success message"); try {success_message = Resolve (success_message);} catch (Exception exception) { error_report = "Unable to resolve reference in field \"" + PROCEDURES_FIELD_NAMES[SUCCESS_MESSAGE_FIELD] + "\"" + NL + exception.getMessage (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Procedure_Status (UNRESOLVABLE_REFERENCE, procedure_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); Close_Log_File (); throw Database_Error (error_report, source_number, procedure_sequence); } // Attempt to match success_message to stdout, stderr. try { Procedure_Field (SUCCESS_MESSAGE_FIELD, success_message); if (stdout_Logger.Buffer ().toString () .matches (success_message) || stderr_Logger.Buffer ().toString() .matches (success_message)) { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure success"); conductor_status = PROCEDURE_SUCCESS; Log_Message ("Procedure completed with success message." + NL + NL); } else { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Procedure failure"); conductor_status = PROCEDURE_FAILURE; Log_Message ("Couldn't match success message regular expression -" + NL + success_message + NL +" resolved from success message pattern -" + NL + Resolver.Pattern () + NL + NL, NOTICE_STYLE); } } catch (Exception exception) { // Success_Message did not resolve to a proper regex. error_report = "Invalid regular expression in field \"" + PROCEDURES_FIELD_NAMES[SUCCESS_MESSAGE_FIELD] + "\"" + NL + " with value: " + Resolver.Pattern () + NL + " resolved to: " + success_message + NL + exception.getMessage (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Procedure_Status (BAD_REGEX, procedure_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); Close_Log_File (); throw Database_Error (error_report, source_number, procedure_sequence); } } if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Reporting procedure record"); Procedure_Status (conductor_status, procedure_status); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); if (conductor_status != PROCEDURE_SUCCESS) break Procedures_Sequence; } // Procedures_Sequence if (conductor_status != PROCEDURE_SUCCESS) { // Run the On_Failure procedure. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Reporting failure"); Log_Message ("Procedure failed." + NL + NL, NOTICE_STYLE); Config_Value (SOURCE_FAILURE_COUNT, new Long (++Source_Failure_Count)); Config_Value (TOTAL_FAILURE_COUNT, new Long (++Total_Failure_Count)); ++Sequential_Failures; Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration) .Sequential_Failures (Sequential_Failures)); String on_failure = Procedure_Field (ON_FAILURE_FIELD); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" " + PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD] + ": " + on_failure); if (on_failure == null || (on_failure = on_failure.trim ()).length () == 0) { Log_Message ("No " + PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD] +" procedure." + NL + NL, NOTICE_STYLE); } else { try { if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Resolving the on failure command"); on_failure = Resolve (on_failure); Procedure_Field (ON_FAILURE_FIELD, on_failure); } catch (Exception exception) { error_report = "Unable to resolve reference in field \"" + PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD] + "\"" + NL + exception.getMessage (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Error report -" + NL + error_report); Log_Message (error_report + NL + NL, NOTICE_STYLE); Close_Log_File (); throw Database_Error (error_report); } // Run the on-failure procedure. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Running the on failure command"); Report_Processing_Event (new Processing_Changes () .Procedure_Record (Procedure_Record)); procedure_status = Run_Procedure (on_failure, 0, ON_FAILURE); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" On failure procedure status: " + procedure_status); if (procedure_status < 0) Log_Message (error_report = PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD] +" procedure failed: " + Status_Conductor_Code_Description (procedure_status) + ' ' + NL + NL, NOTICE_STYLE); else Log_Message (error_report = PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD] +" procedure completed with status " + procedure_status + NL + NL); } if (Stop_on_Failure != 0 && Sequential_Failures >= Stop_on_Failure) Processing_State = HALTED; } else { // Source completed processing successfully. if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println (" Reporting success"); Config_Value (SOURCE_SUCCESS_COUNT, new Long (++Source_Success_Count)); Report_Processing_Event (new Processing_Changes () .Configuration (The_Configuration) .Sequential_Failures (Sequential_Failures = 0)); } // Close the log file. Close_Log_File (); if ((DEBUG & DEBUG_PROCESS_SOURCE) != 0) System.out.println ("<<< Process_Source"); } /*============================================================================== Procedure Processing */ /** Run a system procedure.

The command line is {@link #Parse_Command_Line(String) parsed} into an argument vector and submitted to Runtime.exec. If the exec failed the NO_PROCEDURE status (a negative value) is returned.

The system process ID (PID) is obtained from the resulting Process and used to set the initial status of the procedure.

Logger threads are started to send the stdout and stderr streams from the process to appropriate Writers.

A waitFor on the Process is started that will block until either the procedure completes or the specified timeout period has expired.

When the procedure completes its exit status is obtained. The exit status is expected to be a positive value (including zero). If the procedure times out the exit status is set to PROCEDURE_TIMEOUT, which is a negative value.

The logger threads are always signaled to end and then allowed to finish on their own before returning from this method.

@param command_line The command line String that will run the procedure on the host system. @param time_limit The maximum amount of time, in seconds, to wait for the running process to complete. @param normal_procedure A flag to indicate that the PID of the running process is to be used to set the initial {@link #Procedure_Status(int) Procedure_Status}. @return An status code int. This will be positive if the procedure ran to completion; otherwise a negative Conductor procedure completion code will be returned. @throws Database_Exception if the {@link #Procedure_Status(int) Procedure_Status} could not be set to the PID. @throws IOException if a {@link Log_Message(String, AttributeSet) Log_Message} could not be written. @see Runtime.exec(String[]) */ private int Run_Procedure ( String command_line, int time_limit, boolean normal_procedure ) throws Database_Exception, IOException { if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (">>> Conductor.Run_Procedure:" + NL +" command line: " + command_line + NL +" time limit: " + time_limit + NL +" normal: " + normal_procedure); // Log the start of a new procedure. String description = null; if (normal_procedure) { Log_Message (PROCEDURE_LOG_DELIMITER, MARKER_STYLE); description = Procedure_Field (DESCRIPTION_FIELD); } else Log_Message (ON_FAILURE_PROCEDURE_LOG_DELIMITER, MARKER_STYLE); Log_Message ("Procedure start time: " + new Date () + NL + NL); Log_Message (PROCEDURES_FIELD_NAMES[SEQUENCE_FIELD] + ": " + Procedure_Field (SEQUENCE_FIELD) + NL + ((description == null) ? "" : PROCEDURES_FIELD_NAMES[DESCRIPTION_FIELD] + ": " + description + NL) + (normal_procedure ? PROCEDURES_FIELD_NAMES[COMMAND_LINE_FIELD] : PROCEDURES_FIELD_NAMES[ON_FAILURE_FIELD]) + ": " + command_line + NL + NL, HIGHLIGHT_STYLE); // Attempt to start the procedure. UNIX_Process procedure; try {procedure = new UNIX_Process (Runtime.getRuntime () .exec (Parse_Command_Line (command_line)));} catch (IOException exception) { // Couldn't execute the procedure! String message = exception.getMessage (); if (message == null) message = ""; else message += NL; Log_Message ("Procedure could not be executed." + NL + message + NL, NOTICE_STYLE); if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println ("<<< Run_Procedure: " + NO_PROCEDURE); return NO_PROCEDURE; } catch (NoSuchFieldException exception) { String message = "Unable to obtain the process ID." + NL +"The Process does not appear to be a UNIX Process." + NL + exception.getMessage () + NL; Log_Message (message, NOTICE_STYLE); throw new IOException (ID + NL + message); } if (normal_procedure) Procedure_Status (procedure.ID ()); if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" PID = " + procedure.ID ()); // Start Stream_Logger threads for the procedure's stdout and stderr. stdout_Logger = new Stream_Logger (STDOUT_NAME, procedure.getInputStream (), Logger); stderr_Logger = new Stream_Logger (STDERR_NAME, procedure.getErrorStream (), Logger); stdout_Logger.start (); stderr_Logger.start (); int exit_status = 0; String exit_message = null; SimpleAttributeSet style = null; // Wait for the procedure to complete or timeout. try { // N.B.: Only the least significant 8 bits are used. exit_status = procedure.waitFor (time_limit * 1000) & 0xFF; exit_message = "Procedure completed with status " + exit_status + "."; } catch (IOException exception) { procedure.destroy (); exit_status = PROCEDURE_TIMEOUT; exit_message = "Procedure timeout after " + time_limit + " seconds." + NL + exception.getMessage (); style = NOTICE_STYLE; } catch (InterruptedException exception) { procedure.destroy (); exit_status = PROCEDURE_TIMEOUT; exit_message = "Procedure interrupted!" + NL + exception.getMessage (); style = NOTICE_STYLE; } if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" " + exit_message); // Shutdown the loggers. if (stdout_Logger.isAlive ()) { Sleep (stdout_Logger.Polling_Interval () * 2); if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" stdout_Logger.Close"); stdout_Logger.Close (); while (stdout_Logger.isAlive ()) { if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" Waiting for stdout_Logger to end...."); try {stdout_Logger.join ();} catch (InterruptedException e) {} } } if (stderr_Logger.isAlive ()) { if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" stderr_Logger.Close"); Sleep (stderr_Logger.Polling_Interval () * 2); stderr_Logger.Close (); while (stderr_Logger.isAlive ()) { if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println (" Waiting for stderr_Logger to end...."); try {stderr_Logger.join ();} catch (InterruptedException e) {} } } try { procedure.getInputStream ().close (); procedure.getErrorStream ().close (); procedure.getOutputStream ().close (); } catch (IOException exception) {} Log_Message (NL + "Procedure end time: " + new Date () + NL); Log_Message (exit_message + NL, style); if ((DEBUG & DEBUG_RUN_PROCEDURE) != 0) System.out.println ("<<< Run_Procedure: " + exit_status + NL +" " + exit_message); return exit_status; } /** Parse a String into command line arguments.

The command line arguments are delimited by any combination of space (' '), tab ('\t'), new-line ('\n') or carriage return ('\r') characters. However, character sequences in quotes - either single ('\'') or double ('"') quote characters - remain unbroken.

After parsing each argument String is also scanned to convert escaped characters - preceded by a backslash ('\') - into their unescaped character equivalents.

@param command_line A String to be parsed. @return An array of argument Strings. */ public static String[] Parse_Command_Line ( String command_line ) { if ((DEBUG & DEBUG_PARSE_COMMAND_LINE) != 0) System.out.println (">>> Conductor.Parse_Command_Line: " + command_line); if (command_line == null) command_line = ""; Vector arguments = new Vector (); int start = 0, end, last = command_line.length (); // Collect argument sequences. while (start < last) { // Skip leading whitespace characters. if (COMMAND_LINE_ARGUMENTS_DELIMITERS .indexOf (command_line.charAt (start++)) >= 0) continue; end = --start; String argument = ""; // Seek the next unquoted whitespace character. while (end < last && COMMAND_LINE_ARGUMENTS_DELIMITERS .indexOf (command_line.charAt (end)) < 0) { // Non-whitespace character. char character = command_line.charAt (end); if (character == '"' || character == '\'') { // Quoted sequence. if (start < end) // Append the leading unquoted sequence to the argument. argument += command_line.substring (start, end); start = ++end; // Find the end of the quoted section. while ((end = command_line.indexOf (character, end)) > 0 && command_line.charAt (end - 1) == '\\') // Escaped quote. end++; if (end < 0) { // Unclosed quote; goes to the end of the string. end = last; break; } if (start < end) // Append this quoted sequence to the argument. argument += command_line.substring (start, end); start = ++end; } else end++; } if (start < end) // Append the sequence up to this point to the argument. argument += command_line.substring (start, end); // Remove any escapes. argument = new String_Buffer (argument).escape_to_special ().toString (); arguments.add (argument); start = end; } if ((DEBUG & DEBUG_PARSE_COMMAND_LINE) != 0) System.out.println ("<<< Parse_Command_Line: " + arguments); String[] args_array = new String[arguments.size ()]; arguments.toArray (args_array); return args_array; } /*------------------------------------------------------------------------------ Procedure status */ private void Procedure_Status ( int conductor_status ) throws Database_Exception { String status_value = Status_Indicator (conductor_status); if (Procedure_has_Status) Source_Status.setElementAt (status_value, Source_Status.size () - 1); else { Procedure_has_Status = true; Source_Status.add (status_value); } Update_Source_Record (STATUS_FIELD, Status_Field_Value (Source_Status)); } private void Procedure_Status ( int conductor_status, int procedure_status ) throws Database_Exception { String status_value = Status_Indicator (conductor_status, procedure_status); if (Procedure_has_Status) Source_Status.setElementAt (status_value, Source_Status.size () - 1); else { Procedure_has_Status = true; Source_Status.add (status_value); } Update_Source_Record (STATUS_FIELD, Status_Field_Value (Source_Status)); } /** Parse a Source table Status field value into a Vector of procedure status indicator Strings.

A Source table Status field value contains a comma delimited list of procedure status indicators. Each has the form:

<PID> | <Conductor code>[(<procedure exit status>)]

The PID is the positive, non-zero value of the system's process ID for a running procedure. This value should only be present if the procedure is currently executing.

When the procedure processing has completed the PID is replaced with a Conductor procedure completion code. This will be zero if Conductor determined that the procedure completed successfully. It will be one (1) if the procedure completed unsuccessfully. It will be a negative value if the procedure could not be run to completion for any reason; the code value in this case indicates the reason.

When the procedure has been run to completion, whether successful or not, its exit status value is appended inside parentheses to the Conductor procedure completion code.

@param status_field The String from a Source table Status field value (may be null). @return A Vector of procedure status indicator Strings. */ public static Vector Status_Indicators ( String status_field ) { Vector status = new Vector (); if (status_field != null) { StringTokenizer tokenizer = new StringTokenizer (status_field, ","); while (tokenizer.hasMoreTokens ()) status.add (tokenizer.nextToken ()); } return status; } /** Get the Conductor procedure completion status code value from a procedure status indicator String.

@param status A procedure status indicator String. @return The Conductor procedure completion status code value. @throws NumberFormatException if a value could not be formed. @see #Status_Indicators(String) */ public static int Status_Conductor_Code ( String status ) throws NumberFormatException { if (status != null && status.length () != 0) { int index = status.indexOf ('('); if (index < 0) index = status.length (); if (index > 0) { try {return Integer.parseInt (status.substring (0, index));} catch (NumberFormatException exception) {} } } throw new NumberFormatException ("No Conductor procedure completion code present in \"" + status + "\"."); } /** Get a description String for a Conductor procedure completion code.

If the code value is not a recognized value, the description will be "Unknown procedure completion code ()."

@param code The code value. @return A String describing the meaning of the code value. @see #Status_Conductor_Code(String) */ public static String Status_Conductor_Code_Description ( int code ) { if (code < 0 && -(code + 1) < FAILURE_DESCRIPTION.length) return FAILURE_DESCRIPTION[-(code + 1)]; if (code == PROCEDURE_SUCCESS) return "Procedure success"; if (code == PROCEDURE_FAILURE) return "Procedure failure"; return "Unknown procedure completion code (" + String.valueOf (code) + ")."; } /** Get the procedure exit status value from a procedure status indicator String.

@param status A procedure status indicator String. @return The procedure exit status value. @throws NumberFormatException if a value could not be formed. @see #Status_Indicators(String) */ public static int Status_Procedure_Exit_Value ( String status ) throws NumberFormatException { if (status != null && status.length () != 0) { int start = status.indexOf ('('), end; if (start >= 0) { end = status.indexOf (')', ++start); if (start < end) { try {return Integer.parseInt (status.substring (start, end));} catch (NumberFormatException exception) {} } } } throw new NumberFormatException ("No procedure exit status present in \"" + status + "\"."); } /** Assemble a properly formatted String for a Source table Status field value.

This method is the converse of the {@link #Status_Indicators(String) Status_Indicators} method.

@param status A Vector of procedure status indicator Strings. @return A String suitably formatted for a Source table Status field value. */ public static String Status_Field_Value ( Vector status ) { String field_value = ""; for (int index = 0; index < status.size (); index++) { if (field_value.length () != 0) field_value += ","; field_value += status.get (index); } return field_value; } /** Assemble a properly formatted procedure status indicator String as used in a Sources table Status field value.

@param conductor_status A conductor procedure completion code. @param procedure_status A procedure exit status value. @return A String suitably formatted for a Source table Status field value. */ public static String Status_Indicator ( int conductor_status, int procedure_status ) { return String.valueOf (conductor_status) + '(' + String.valueOf (procedure_status) + ')'; } /** Assemble a properly formatted procedure status indicator String as used in a Sources table Status field value.

@param conductor_status A conductor procedure completion code. @return A String suitably formatted for a Source table Status field value. */ public static String Status_Indicator ( int conductor_status ) {return String.valueOf (conductor_status);} /*============================================================================== Logging */ // Management implementation. /** Register a Writer to receive processing log stream output.

The Conductor writes its processing log reports, including the output from all pipeline procedures it runs, to all registered log Writers.

@param writer A Writer object. @return This Conductor Management object. @see #Enable_Log_Writer(Writer, boolean) @see #Remove_Log_Writer(Writer) */ public Management Add_Log_Writer ( Writer writer ) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">-< Conductor.Add_Log_Writer:" + NL + writer + NL +" Styled_Writer: " + (writer instanceof Styled_Writer)); Logger.Add (writer); return this; } // Management implementation. /** Unregister a log Writer.

@param writer A Writer object. @return true If the writer was registered and is now removed; false if it was not registered. @see #Add_Log_Writer(Writer) */ public boolean Remove_Log_Writer ( Writer writer ) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">-< Conductor.Remove_Log_Writer: " + writer); return Logger.Remove (writer); } // Management implementation. /** Enable or disable output to a {@link #Add_Log_Writer(Writer) registered log stream Writer}.

@param writer A Writer that has been registered to receive Conductor log stream output. If the writer is not {@link #Add_Log_Writer(Writer) registered} to receive the Conductor log stream nothing is done. @param enable If false, Conductor log stream output to the Writer is suspended without having to unregister the Writer. If true, a Writer that has had its log stream output suspended will begin receiving it again. @return This Conductor Management object. */ public Management Enable_Log_Writer ( Writer writer, boolean enable ) { Logger.Suspend (writer, ! enable); return this; } /** Logs a message to the Logger.

@param message The message String to write to the Logger. @param style An AttributeSet style to apply to the message. This may be null to use the default text style. @throws IOException if the Log_File_Writer could not be written. If a Writer other than the Log_File_Writer throws an exception it is closed and {@link #Remove_Log_Writer(Writer) removed} from the Logger. */ protected void Log_Message ( String message, AttributeSet style ) throws IOException { try {Logger.Write (message, style);} catch (Multiwriter_IOException exception) { // Check if the log file threw an exception. boolean log_file_exception = false; Vector writers = exception.Sources; Writer writer; int index = writers.size (); while (--index >= 0) { writer = writers.get (index); if (writer == Log_File_Writer) log_file_exception = true; else { try {writer.close ();} catch (IOException exeption) {} Remove_Log_Writer (writer); } } if (log_file_exception) { // The local Log_File_Writer threw an exception. Close_Log_File (); throw (IOException)exception.Exceptions.get (index); } } } /** Logs a message to the Logger.

The default text style is used.

@param message The message String to write to the Logger. @throws IOException if the Log_File_Writer could not be written. @see #Log_Message(String, AttributeSet) */ protected void Log_Message ( String message ) throws IOException {Log_Message (message, null);} private void Close_Log_File () { if (Log_File_Writer != null) { // Remove the log file writer from the Logger. Remove_Log_Writer (Log_File_Writer); try {Log_File_Writer.flush ();} catch (IOException exception) {} try {Log_File_Writer.close ();} catch (IOException exception) {} } Log_File_Writer = null; } private void Stop_Logging () { Close_Log_File (); try {Logger.flush ();} catch (IOException exception) {} try {Logger.close ();} catch (IOException exception) {} Logger.Remove_All (); } /*============================================================================== Stage_Manager */ /** Flag that determines if the Conductor requires a Stage_Manager.

If true and a Stage_Manager connection can not be established the Conductor will throw an exception; otherwise the Conductor will proceed without a Stage_Manager.

The initial value is false; */ public static boolean Require_Stage_Manager = false; private Local_Theater Theater_Management = new Local_Theater (this); private String Stage_Manager_Key = null; private String Connect_to_Stage_Manager () throws IOException { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Connect_to_Stage_Manager"); String report = null; try {Theater_Management.Open (Stage_Manager_Key);} catch (IOException exception) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) { System.out.println (" Exception: " + exception); if (exception instanceof ConnectException) System.out.println (" ConnectException"); if (exception instanceof Theater_Protocol_Exception) System.out.println (" Theater_Protocol_Exception: Reason " + ((Theater_Protocol_Exception)exception).Reason ()); } if (Require_Stage_Manager || (! (exception instanceof ConnectException) && (exception instanceof Theater_Protocol_Exception && ((Theater_Protocol_Exception)exception).Reason () != Theater_Protocol_Exception.TIMEOUT))) { Theater_Management.Auto_Open (false); if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Exception being thrown"); throw exception; } report = NL +"Note: A connection to the Stage_Manager could not be established." + NL + exception.getMessage () + NL; } if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" report -" + NL + report + NL + "<<< Connect_to_Stage_Manager"); return report; } private void Disconnect_from_Stage_Manager () { if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (">>> Conductor.Disconnect_from_Stage_Manager" + NL +" Theater_Management.Auto_Open (false) ..."); // Prevent auto-open when Done. Theater_Management.Auto_Open (false); if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Stop_Logging ..."); Stop_Logging (); if (The_Database != null) { if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" The_Database.Disconnect ..."); try {The_Database.Disconnect ();} catch (Database_Exception exception) {} } if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Theater_Management.Close ..."); Theater_Management.Close (); if ((DEBUG & DEBUG_EXIT) != 0) System.out.println ("<<< Conductor.Disconnect_from_Stage_Manager"); } // Management implementation. /** Test if the Conductor is connected to a Stage_Manager.

@return true if the Conductor is connected to a Stage_Manager via an open Local_Theater; false otherwise. @see Local_Theater */ public boolean Connected_to_Stage_Manager () {return Theater_Management.Opened ();} private Message Conductor_Identity = null; // Management implementation. /** Get the identity description Message for this Conductor.

The identity Message contains the following parameters:

{@link Message#ACTION_PARAMETER_NAME}
Indicates an identity Message with the {@link Message#IDENTITY_ACTION} value.
{@link Message#NAME_PARAMETER_NAME}
The identity name is {@link #CONDUCTOR_GROUP}.
{@link #HOSTNAME_PARAMETER}
The {@link Host#FULL_HOSTNAME} of the host system.
{@link #CONDUCTOR_ID_PARAMETER}
The Conductor ID is the {@link Host#SHORT_HOSTNAME} followed by a colon (':') and the system process ID of this Conductor. If the process ID can not be obtained only the short hostname is included.
{@link #CATALOG_PARAMETER}
The name of the Database catalog where the pipeline tables are located.
{@link #PIPELINE_PARAMETER}
The name of the Conductor pipeline being managed.
{@link #CONFIGURATION_SOURCE_PARAMETER}
The {@link Configuration#Source() source} of the Configuration that is being used.
{@link Message#CLASS_ID_PARAMETER_NAME}
The {@link #ID} of this Conductor class.
{@link #DATABASE_SERVER_PARAMETER}
Provides the name of the Configuration parameter group that contains the database access parameters.
A parameter group named the same as the value of the {@link #DATABASE_SERVER_PARAMETER}. This group contains the following parameters:

{@link Database#TYPE}
The value is taken from the {@link Database#Configuration() database Configuration} parameter of the same name.
{@link Configuration#HOST}
The value is taken from the {@link Database#Configuration() database Configuration} parameter of the same name. However, if the value is "localhost" then the {@link Host#FULL_HOSTNAME} is used instead.
{@link Configuration#USER}
The value is taken from the {@link Database#Configuration() database Configuration} parameter of the same name.

@return A Message containing the idenity description parameters for this Conductor. */ public Message Identity () { if (Conductor_Identity == null) { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (">>> Identity"); Conductor_Identity = Message .Identity () .Set (Message.NAME_PARAMETER_NAME, CONDUCTOR_GROUP) .Set (HOSTNAME_PARAMETER, Host.FULL_HOSTNAME) .Set (CONDUCTOR_ID_PARAMETER, Conductor_ID) .Set (CATALOG_PARAMETER, Catalog) .Set (PIPELINE_PARAMETER, Config_Value (PIPELINE_PARAMETER)) .Set (CONFIGURATION_SOURCE_PARAMETER, Config_Value (CONFIGURATION_SOURCE_PARAMETER)) .Set (Message.CLASS_ID_PARAMETER_NAME, ID); try { if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (" Database Configuration -" + NL + The_Database.Configuration ().Description ()); String database_hostname = The_Database.Configuration ().Get_One (Database_Server_Name + Configuration.Path_Delimiter () + Configuration.HOST); if ("localhost".equals (database_hostname)) database_hostname = Host.FULL_HOSTNAME; Conductor_Identity .Set (DATABASE_SERVER_PARAMETER, Database_Server_Name) .Add (new Parameter (Database_Server_Name) .Add (new Parameter (Database.TYPE) .Value (The_Database.Configuration ().Get_One (Database.TYPE))) .Add (new Parameter (Configuration.HOST) .Value (database_hostname)) .Add (new Parameter (Configuration.USER) .Value (The_Database.Configuration ().Get_One (Configuration.USER)))); } catch (PVL_Exception exception) {} if ((DEBUG & DEBUG_MANAGEMENT) != 0) System.out.println (Conductor_Identity.Description () + NL + "<<< Identity"); } return Conductor_Identity; } /*============================================================================== Helpers */ private static String Error_Message ( String message ) { String prefix = ID + NL + "Conductor ID: " + Conductor_ID; if (message == null) message = prefix; else if (message.indexOf (ID) < 0) message = prefix + NL + message; return message; } private Database_Exception Database_Error ( String message, String source_number, String procedure_sequence ) { String database_type = Config_Value (DATABASE_TYPE_PARAMETER), database_host = Config_Value (DATABASE_HOSTNAME_PARAMETER), report = "Problem with " + ((database_type == null) ? "" : (database_type + ' ')) + "database " + ((database_host == null) ? "" : ("on host " + database_host)) + NL + "in pipeline " + Pipeline; if (source_number != null) report += " for source file number " + source_number; if (procedure_sequence != null) report += " at procedure sequence " + procedure_sequence; report += "." + NL + message; return new Database_Exception (Error_Message (report)); } private Database_Exception Database_Error ( String message, String source_number ) {return Database_Error (message, source_number, null);} private Database_Exception Database_Error ( String message ) {return Database_Error (message, null, null);} private static class Status_Report { public Conductor The_Conductor = null; public String Message = null; public int Status = EXIT_SUCCESS; public Status_Report ( Conductor conductor ) {The_Conductor = conductor;} public Status_Report ( int status ) {Status = status;} public Status_Report ( Conductor conductor, String message, Exception exception ) {this (conductor, message, exception, 0);} public Status_Report ( Conductor conductor, String message, Exception exception, int status ) { The_Conductor = conductor; Message = message; Status = status; if (exception != null) { if (message == null) message = ""; else message += NL; String when = " occured " + ((conductor == null) ? "while constructing the Conductor." : "during pipeline processing."); if (exception instanceof Configuration_Exception) { Status = EXIT_CONFIGURATION_PROBLEM; Message = Error_Message (message + "A Configuration problem" + when + NL + exception.getMessage ()); } else if (exception instanceof Database_Exception) { Status = EXIT_DATABASE_PROBLEM; Message = Error_Message (message + "A database problem occured" + when + NL + exception.getMessage ()); } else if (exception instanceof IOException) { Status = EXIT_IO_FAILURE; Message = Error_Message (message + "An I/O problem occured" + when + NL + exception.getMessage ()); } else { Status = EXIT_UNEXPECTED_EXCEPTION; StringWriter report_writer = new StringWriter (); exception.printStackTrace (new PrintWriter (report_writer, true)); Message = Error_Message (message + "An unexpected exception occured" + when + NL + report_writer.toString ()); } } else if (conductor != null && conductor.Stop_on_Failure () > 0 && conductor.Stop_on_Failure () == conductor.Sequential_Failures ()) { if (message == null) message = ""; else message += NL; Status = EXIT_TOO_MANY_FAILURES; Message = Error_Message (message + "Too many sequential source processing failures (" + conductor.Sequential_Failures + ")."); } } } // Status_Report /*============================================================================== Application main */ /** Instantiate a Conductor application.

@param args The Conductor {@link #Usage() command line} arguments. */ public static void main ( String[] args ) { Configuration configuration = null; configuration.Duplicate_Parameter_Action_Default = DEFAULT_DUPLICATE_PARAMETER_ACTION; String database_server_name = null, pipeline = null, catalog = null; boolean managed = false, wait_to_start = false; if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("*** " + ID + " ***"); for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].toUpperCase ().charAt (1)) { case 'P': // Pipeline. if (++count == args.length) { stdout.println ("Missing pipeline name."); Usage (); } if (pipeline != null) { stdout.println ("Multiple pipelines specified -" + NL +" " + pipeline + " and " + args[count]); Usage (); } pipeline = args[count]; break; case 'C': if (args[count].length () > 2 && (args[count].charAt (1) == 'A' || args[count].charAt (2) == 'a')) { // Catalog. if (++count == args.length) { stdout.println ("Missing catalog name."); Usage (); } catalog = args[count]; break; } // Configuration. if (++count == args.length) { stdout.println ("Missing configuration filename."); Usage (); } if (configuration != null) { stdout.println ("Multiple configuration files specified -" + NL +" " + configuration.Source () + " and " + args[count]); Usage (); } try {configuration = new Configuration (args[count]);} catch (Configuration_Exception exception) { stdout.println ("Unable to construct the configuration for \"" + args[count] + "\"." + NL + exception.getMessage ()); Exit (EXIT_CONFIGURATION_PROBLEM); } catch (IllegalArgumentException exception) { stdout.println ("Unable to find the configuration file." + NL + exception.getMessage ()); Exit (EXIT_CONFIGURATION_PROBLEM); } break; case 'D': // Database case 'S': // Server if (++count == args.length) { stdout.println ("Missing database server name."); Usage (); } if (database_server_name != null) { stdout.println ("Multiple database servers specified -" + NL +" " + database_server_name + " and " + args[count]); Usage (); } database_server_name = args[count]; break; case 'M': // Managed/Monitored with GUI. managed = true; break; case 'W': // Wait for Management Start. wait_to_start = true; break; case 'V': // Version. stdout.println (ID); Exit (EXIT_SUCCESS); default: stdout.println ("Unrecognized option: " + args[count]); case 'H': // Help Usage (); } } else { // Pipeline. if (pipeline != null && ! pipeline.equals (args[count])) { stdout.println ("Multiple pipelines specified -" + NL +" " + pipeline + " and " + args[count]); Usage (); } pipeline = args[count]; } } if (pipeline == null) { stdout.println ("No pipeline specified."); Usage (); } if (catalog != null) { String name = Reference_Resolver.Catalog_Name (pipeline); if (name != null) { if (! name.equals (catalog)) { stdout.println ("Multiple database catalogs specified -" + NL +" From the -catalog option: " + catalog + NL +" From the pipeline name: " + name); Exit (EXIT_COMMAND_LINE_SYNTAX); } } else pipeline = catalog + Reference_Resolver.COMPONENTS_DELIMITER + pipeline; } if (configuration == null) { try {configuration = new Configuration ((String)null);} catch (Configuration_Exception exception) { stdout.println ("Unable to construct the configuration for \"" + Configuration.Default_Source () + "\"." + NL + exception.getMessage ()); Exit (EXIT_CONFIGURATION_PROBLEM); } catch (IllegalArgumentException exception) { stdout.println ("Unable to find the configuration file." + NL + exception.getMessage ()); Exit (EXIT_CONFIGURATION_PROBLEM); } } // Construct the Conductor. Conductor conductor = null; try {conductor = new Conductor (pipeline, configuration, database_server_name);} catch (Exception exception) { Exit (new Status_Report (null, "Unable to construct the Conductor for pipeline " + pipeline + " on " + Host.FULL_HOSTNAME + '.', exception)); } if (managed) { // Give the Conductor to the GUI manager/monitor. new Manager (conductor, configuration); if (wait_to_start) // Let the listener know that the Conductor is ready. stdout.println (">>> READY <<<"); } else { // No local manager. stdout.println ("Conductor 2.47 (2012/04/16 06:04:09) on " + Conductor_ID); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Starting pipeline processing..."); // Semaphore thread to wait for pipeline processing to end. conductor.Main_Thread_Waiting = new Thread (new Runnable () {public void run () {try {synchronized (this) {wait ();}} catch (InterruptedException except) {}}}); conductor.Main_Thread_Waiting.start (); // Let the listener know that the Conductor is ready. stdout.println (">>> READY <<<"); if (! wait_to_start) conductor.Start (); // Wait for pipeline processing to end. while (conductor.Main_Thread_Waiting.isAlive ()) { try {conductor.Main_Thread_Waiting.join ();} catch (InterruptedException except) {} } Exit (new Status_Report (conductor, null, conductor.Processing_Exception ())); } } private static void Exit ( Status_Report status_report ) { if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (">>> Conductor.Exit"); int status = EXIT_SUCCESS; if (status_report != null) { if (status_report.Message != null) stdout.println (status_report.Message); Send_Failure_Notification (status_report); if (status_report.The_Conductor != null) { // Clean up. Processing_Changes processing_changes = new Processing_Changes () .Exiting (true); if (status_report.Status != EXIT_SUCCESS && status_report.Message != null) processing_changes.Error_Condition (status_report.Message); if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Report_Processing_Event"); status_report.The_Conductor .Report_Processing_Event (processing_changes); if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Disconnect_from_Stage_Manager ..."); status_report.The_Conductor.Disconnect_from_Stage_Manager (); } status = status_report.Status; } // Drain any pending output. if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Flush standard output"); System.out.flush (); System.err.flush (); if ((DEBUG & DEBUG_EXIT) != 0) System.out.println (" Exiting with status " + status); System.exit (status); } private static void Exit ( int status ) {Exit (new Status_Report (status));} private static void Exit () {Exit (null);} private static void Send_Failure_Notification ( Status_Report status_report ) { if ((DEBUG & DEBUG_NOTIFICATION) != 0) System.out.println (">>> Conductor.Send_Failure_Notification"); if (Notify_List != null && status_report != null && status_report.Status != EXIT_SUCCESS) { String message = "A failure condition has halted Conductor processing" + NL + "of the " + Pipeline + " pipeline on " + Conductor_ID + ((status_report.Message == null) ? "." : (" -" + NL + NL + status_report.Message)); if ((DEBUG & DEBUG_NOTIFICATION) != 0) System.out.println (" Constructing Notify with Notify_List: " + Notify_List + NL +" Message -" + NL + message); Notify notifier = null; try {notifier = new Notify (Notify_List);} catch (NoClassDefFoundError error) { message = message.concat (NL + NL + "!!! Email notification failed!" + NL + "A class required by the Notify object could not be found."); if (error.getMessage () != null) message = message.concat (NL + error.getMessage ()); if (status_report.The_Conductor != null) { try {status_report.The_Conductor .Log_Message (message, NOTICE_STYLE);} catch (IOException exception) {} } if ((DEBUG & DEBUG_NOTIFICATION) != 0) System.out.println (NL + message + NL + NL + "status_report.Th_Conductor != null? " + (status_report.The_Conductor != null)); } if (notifier != null) { if (notifier .Subject ("Conductor " + Pipeline + " pipeline halted") .Message (message) .Deliver () != Notify_List.size () && status_report.The_Conductor != null) { message = message.concat (NL + NL + "!!! Email notification problem!" + NL + "Unable to deliver message to " + notifier.Undeliverable ()); try {status_report.The_Conductor .Log_Message (message, NOTICE_STYLE);} catch (IOException exception) {} } } } if ((DEBUG & DEBUG_NOTIFICATION) != 0) System.out.println ("<<< Conductor.Send_Failure_Notification"); } /** Prints the command line usage syntax.

Usage: Conductor <Switches>
  Switches -
    [-Pipeline] <pipeline>
    [-Configuration <source>]
    [-Database|-Server <server name>]
    [-CAtalog <catalog>]
    [-Monitor]
    [-Wait-to-start]
    [-Version]
    [-Help]

The -Pipeline switch specifies which pipeline the Conductor is to manage. A pipeline must be specified. A command line argument without a preceding switch name is assumed to be the pipeline name. The pipeline name has the form:

[<catalog>.]<pipeline>

where <pipeline> is the name of the pipeline and is used as the prefix of the Sources and Procedures table names:

<pipeline>_Sources
<pipeline>_Procedures

The -Configuration option is used to specify the filename, or URL (http or ftp), where the configuration parameters are to be found. If this option is not specified the "Conductor.conf" filename will be used. If the configuration file is not in the current working directory, it will be looked for in the user's home directory.

The configuration file must contain the necessary database access information needed to identify and connect with the database server (as required by the {@link PIRL.Database.Database#Database(Configuration) Database} constructor and its {@link PIRL.Database.Database#Connect() Connect} method). The database "Type" parameter must be provided that specifies the type of database server (e.g. "MySQL") that will be accessed. Additional database access parameters typically provided are the server "Host" name and database "User" and "Password" access values. Depending on the type of database server and the driver and its Data_Port implementation (e.g. {@link PIRL.Database.MySQL_Data_Port}) there may be other required and optional parameters that can be included, such as a "Port" parameter to specify the database host system's network port to use for server communications.

In addition to the "Catalog" parameter (described below), the "Log_Directory" parameter may optionally be located in the "/Conductor" group. The "Log_Directory" parameter specifies the directory where log files will be written. The value of this parameter may contain embedded references for the database {@link Reference_Resolver Reference_Resolver}. Other parameters that will be used if present in the Conductor configuration group are described in the Conductor control parameters section of the Conductor class description.

The -Server or -Database option may be used to specify the Group of database server access parameters in the configuration file. A configuration file may contain more than one Group of database server access parameters where the name of each such Group must be in the Server parameter list. By default the first name in the Server list is the Group of database access parameters that will be used.

The -CAtalog option may be used to specify the name of the database catalog containing the pipeline tables. However, it is ambiguous command line syntax to use this option and include a different <catalog> in the pipeline name.

The <catalog> name may alternatively be specified by a "Catalog" parameter in the configuration file. This parameter is first sought in the "/Conductor" parameters Group, then in the database server parameters Group or any parent of that group. It is necessary that a <catalog> name be found somewhere.

The -Manage or -Monitor option may be used to run Conductor with a Manager GUI. In this mode Conductor will not proceed to process the pipeline immediately. Instead, the Manager will enable processing to be started, stopped, aborted, and the pipeline processing continuously monitored. By default the Conductor is run without a Manager.

The -Wwait-to-start option will cause the Conductor to remain in the wait state until it receives a message to start source record processing. If the Conductor is run with a Manager -wait-to-start is implicit. Remote management can be provided via a Stage_Manager and a Kapellmeister client. By default the Conductor will not wait-to-start unless it is run with a Manager.

The -Version option will cause the Conductor version identification to be listed without running the Conductor.

The -Help option will list the brief command line syntax usage and then exit.

N.B.: This method always results in a System.exit with the {@link #EXIT_COMMAND_LINE_SYNTAX} status value. */ public static void Usage () { System.out.println ("Usage: Conductor " + NL +" Switches -" + NL +" [-Pipeline] " + NL +" [-Configuration ]" + NL +" Default: Conductor.conf" + NL +" [-Server ]" + NL +" Default: First Server name in the Configuration" + NL +" [-CAtalog ]" + NL +" Default: The Catalog prefix of the Pipeline name" + NL +" or Catalog name in the Configuration" + NL +" [-Manager | Monitor]" + NL +" Default: No Manager" + NL +" [-Wait-to-start]" + NL +" Default: Start immediately if no Manager" + NL +" [-Version]" + NL +" [-Help]" + NL ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Conductor/Conductor.conf0000644000175000017500000000701111607745647017701 0ustar mathieumathieu# Conductor Configuration # # PIRL CVS ID: Conductor.conf,v 1.22 2011/07/15 05:09:59 castalia Exp /* Database server definitions: */ Server = (localhost, PIRL) Group = localhost # Type of database server (Data_Port). Type = MySQL # Name of the database server host system. Host = localhost # Database system user account. User = "" # Database server password (NOT the host system password). Password = "" End_Group Group = PIRL Type = MySQL Host = PIRLdb.LPL.Arizona.edu User = "" Password = "" End_Group /* Conductor application parameters. */ Group = Conductor /* The default pipeline name. */ Pipeline = Test /* The default pipeline database catalog. */ Catalog = Proc_Test /* The default directory for writing log files. */ Log_Directory = logs /* Empty Procedures Success_Status field implies any status is success. */ Empty_Success_Any = true /* The number of sequential source failures that will stop processing. */ Stop_on_Failure = 3 /* Who to notify by email of an unexpected processing halt. */ Notify = () /* The rate (seconds) at which to poll for new source records. Zero means don't poll; stop when no sources are available. */ Poll_Interval = 5 /* The minimum number of source records to process in batch mode. Must be at least 1. */ Min_Source_Records = 1 /* The maximum number of source records to obtain from the sources table when the cache is refilled. Must be >= Min_Source_Records. */ Max_Source_Records = 1000 /* Stage_Manager connection parameters. */ Group = Stage_Manager /* Is a Stage_Manager connection required to start the Conductor? */ Require_Stage_Manager = false /* The password for connecting to the Stage_Manager. If no password is provided and it is required by the Stage_Manager no Stage_Manager connection will be possible. */ Password = "" /* The port number to use when connection to the Stage_Manager. */ Port = 4144 /* The maximum time (seconds) allowed for a Stage_Manager Message receipt to be completed. */ Timeout = 10 /* Hello broadcast. The port number and multicast address to use when listening for a Stage_Manager startup Hello message during auto-open. If a port number is not provided or not positive a default value will be used. If an address is not provided or it is empty a default value will be used. */ Hello_Port = 4170 Hello_Address = "230.1.2.3" End_Group # The following parameters are used by the Conductor Manager: /* Display the splash screen during Manager startup. This may be a time in seconds or a true/false (enabled/disabled) value. */ Splash_Screen = true /* Manager window size and display location. */ Manager_Width = 750 Manager_Height = 463 Manager_Location_X = 300 Manager_Location_Y = 100 /* Manager Log Monitor window size. */ Monitor_Width = 700 Monitor_Height = 400 /* Display tooltips on Manager GUI components. */ Tooltips = true End_Group pirl-2.3.8/PIRL/Conductor/Sources_Table_Model.java0000644000175000017500000000712011742733132021572 0ustar mathieumathieu/* Sources_Table_Model PIRL CVS ID: Sources_Table_Model.java,v 1.4 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import javax.swing.table.DefaultTableModel; import java.util.Vector; /** A Sources_Table_Model contains a table of Conductor Source records.

@author Bradford Castalia, UA/PIRL @version 1.4 */ public class Sources_Table_Model extends DefaultTableModel { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor..Sources_Table_Model (1.4 2012/04/16 06:04:10)"; public boolean Editable = false; public int Current_Row = -1; private static final String NL = System.getProperty ("line.separator"); /*============================================================================== Constructors */ public Sources_Table_Model () {} public Sources_Table_Model ( String[][] records, String[] field_names ) {super (records, field_names);} public Sources_Table_Model ( String[] field_names ) {super (field_names, 0);} public Sources_Table_Model ( Vector records, Vector field_names ) {super (records, field_names);} public Sources_Table_Model ( Vector field_names ) {super (field_names, 0);} /*============================================================================== TableModel */ public Class getColumnClass ( int column ) { if (column >= 0 && column < getColumnCount ()) return String.class; return Object.class; } public Object getValueAt ( int row, int column ) { if (row < 0 || row >= getRowCount () || column < 0 || column >= getColumnCount ()) return null; return ((Vector)dataVector.get (row)).get (column); } public boolean isCellEditable ( int row, int column ) {return Editable;} /*============================================================================== Accessors */ public Vector Record ( int row ) { if (row >= 0 && row < getRowCount ()) return (Vector)dataVector.get (row); return null; } public Sources_Table_Model Record ( int row, Vector record ) { if (record == null) return this; if (row < 0 || row > getRowCount ()) throw new ArrayIndexOutOfBoundsException (ID + NL + "Unable to set the record at row " + row + '.' + NL + "The valid row range is 0 - " + getRowCount () + " inclusive."); if (record.size () != getColumnCount ()) throw new ArrayIndexOutOfBoundsException (ID + NL + "Unable to set the record at row " + row + '.' + NL + "The " + record.size () + " element record size does not match the " + getColumnCount () + " column table size."); if (row == getRowCount ()) addRow (record); else { dataVector.set (row, record); fireTableRowsUpdated (row, row); } return this; } } pirl-2.3.8/PIRL/Conductor/Sources_Table.java0000644000175000017500000001737311742733132020465 0ustar mathieumathieu/* Sources_Table PIRL CVS ID: Sources_Table.java,v 1.6 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import org.jdesktop.swingx.JXTable; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.table.TableCellRenderer; import javax.swing.JLabel; import javax.swing.BorderFactory; import javax.swing.JViewport; import java.awt.Component; import java.awt.Color; import java.awt.Font; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; import java.util.Vector; /** A Sources_Table is a table view of a Sources_Table_Model.

@author Bradford Castalia, UA/HiROC @version 1.6 */ public class Sources_Table extends JXTable { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Sources_Table (1.6 2012/04/16 06:04:10)"; private Font Emphasis_Font; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_RENDERER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Sources_Table ( Sources_Table_Model table_model ) { super (table_model); setColumnSelectionAllowed (true); setRowSelectionAllowed (false); getColumnModel ().getSelectionModel () .setSelectionMode (ListSelectionModel.SINGLE_SELECTION); addMouseListener (new Table_Mouse_Listener ()); setBackground (Colors.TABLE); getTableHeader ().setBackground (Colors.TABLE_HEADER); Emphasis_Font = getFont ().deriveFont (Font.BOLD); /* WARNING: These are JXTable specific methods. */ setColumnControlVisible (true); setSortable (false); setDefaultRenderer (String.class, new Sources_Table_Cell_Renderer ()); } /*============================================================================== Accessors */ /* If not provided by the base class. public int convertRowIndexToModel ( int view_row ) {return view_row;} */ /*============================================================================== Sources_Table_Cell_Renderer */ private class Sources_Table_Cell_Renderer extends JLabel implements TableCellRenderer { public Sources_Table_Cell_Renderer () { setOpaque (true); setBorder (BorderFactory.createEmptyBorder (0, 5, 0, 0)); setFont (Emphasis_Font); } public Component getTableCellRendererComponent ( JTable table, Object value, boolean selected, boolean focused, int row, int column ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (">>> Sources_Table_Cell_Renderer: " + row + '/' + ((JXTable)table).convertRowIndexToModel (row) + ',' + column + '/' + table.convertColumnIndexToModel (column) + ' ' + value); row = ((JXTable)table).convertRowIndexToModel (row); column = table.convertColumnIndexToModel (column); Sources_Table_Model model = (Sources_Table_Model)table.getModel (); Vector record = (Vector)model.Record (row); Color color = table.getBackground (); String field = (String)record.get (Conductor.CONDUCTOR_ID_FIELD), annotation = null; if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" " + Conductor.CONDUCTOR_ID_FIELD + ": " + field); if (field != null && field.length () != 0) { // Acquired record. if (row == model.Current_Row) // The currently selected source record. color = Colors.CURRENT_RECORD; else color = Colors.PROCESSED_RECORD; if (column == Conductor.STATUS_FIELD) { field = Current_Status ((String)record.get (Conductor.STATUS_FIELD)); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" " + Conductor.STATUS_FIELD + ": " + field); if (field != null) { int code = Conductor.PROCEDURE_FAILURE + 1; try {code = Conductor.Status_Conductor_Code (field);} catch (NumberFormatException exception) {} if (code <= Conductor.PROCEDURE_FAILURE) { annotation = Conductor.Status_Conductor_Code_Description (code); if (code != Conductor.PROCEDURE_SUCCESS) { // Failure status. if (code == Conductor.PROCEDURE_FAILURE) color = Colors.FAILURE_STATUS_FIELD; else color = Colors.FAILED_SOURCE_FIELD; } } } } } if (selected) { if (color.getRed () > 200 && color.getGreen () > 200 && color.getBlue () > 200) color = color.darker (); else color = color.brighter (); } setBackground (color); setValue (value); setToolTipText (annotation); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("<<< Sources_Table_Cell_Renderer"); return this; } public void setValue ( Object value ) {setText ((value == null) ? "" : value.toString ());} } // Sources_Table_Cell_Renderer private static String Current_Status ( String status_field_value ) { String status = null; Vector status_indicators = Conductor.Status_Indicators (status_field_value); if (! status_indicators.isEmpty ()) status = (String)status_indicators.lastElement (); return status; } /** Scrolls a table to show a specified row.

If the table is not contained withing a scrollable pane (typically a JScrollPane) nothing is done. If the specified row is already visible nothing is done.

@param table The JTable to have the row set visible. @param row The row index, where zero is the top row, of the row to be made visible. If the row is less than zero the top row will be made visible. If the row is greater than or equal to the number of rows in the table the last row will be make visible. */ public static void Show_Table_Row ( JTable table, int row ) { if (! (table.getParent () instanceof JViewport)) return; JViewport viewport = (JViewport)table.getParent (); Point point = viewport.getViewPosition (); Rectangle rectangle = table.getCellRect (row, 0, true); rectangle.setLocation (rectangle.x - point.x, rectangle.y - point.y); viewport.scrollRectToVisible (rectangle); } /** Tests if a specified row of a table is visible.

False will be returned if the table is not contained within a scrollable pane (typically a JScrollPane), the table has no rows, the row index is less than zero or is greater than or equal to the number of rows in the table.

A row is visible if the entire row height is visible even if any part of the row width is not visible.

@param table The JTable to have the row set visible. @param row The row index, where zero is the top row, of the row to test. */ public static boolean Table_Row_Is_Visible ( JTable table, int row ) { if (! (table.getParent () instanceof JViewport)) return false; int rows = table.getRowCount () - 1; if (rows < 0 || row < 0 || row > rows) return false; JViewport viewport = (JViewport)table.getParent (); Rectangle cell = table.getCellRect (row, 0, true), view = new Rectangle (viewport.getViewPosition (), viewport.getExtentSize ()); if (cell.y < view.y || (cell.y + cell.height) > (view.y + view.height)) return false; return true; } } pirl-2.3.8/PIRL/Conductor/Conductor10000755000175000017500000000524011062100241017005 0ustar mathieumathieu#!/bin/csh -f # # CVS ID: Conductor1,v 2.1 2008/09/11 02:21:21 castalia Exp # # Conductor1 # # A wrapper for the Java process management application, first generation. # Location of the PIRL Java Packages. if (! $?PIRL_JAVA_HOME) set PIRL_JAVA_HOME = /opt/java if (! -e ${PIRL_JAVA_HOME}) then echo "No such file or directory: ${PIRL_JAVA_HOME}" exit -1 endif set classpath = $PIRL_JAVA_HOME # Database drivers: # Location of the MySQL JDBC driver. if (! $?MySQL_JDBC) then if ($PIRL_JAVA_HOME !~ "*.jar") \ set MySQL_JDBC = $PIRL_JAVA_HOME/mysql-connector endif if ($?MySQL_JDBC) then if (! -e ${MySQL_JDBC}) then echo "No such file or directory: ${MySQL_JDBC}" exit -1 endif set classpath = ${classpath}:${MySQL_JDBC} endif # Location of the PostgreSQL JDBC driver. if (! $?PostgreSQL_JDBC) then if ($PIRL_JAVA_HOME !~ "*.jar") \ set PostgreSQL_JDBC = $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar endif if ($?PostgreSQL_JDBC) then if (! -e ${PostgreSQL_JDBC}) then echo "No such file or directory: ${PostgreSQL_JDBC}" exit -1 endif set classpath = ${classpath}:${PostgreSQL_JDBC} endif # External Java packages: # Location of the Java Components for Mathematics. if (! $?JCM) then if ($PIRL_JAVA_HOME !~ "*.jar") \ set JCM = $PIRL_JAVA_HOME/jcm endif if ($?JCM) then if (! -e ${JCM}) then echo "No such file or directory: ${JCM}" exit -1 endif set classpath = ${classpath}:${JCM} else echo "The Java Components for Mathematics are required by Conductor." echo "See http://math.hws.edu/javamath" exit -1 endif # Apple's OS X JVM won't allow Java classes to be run remotely # if they employ any graphics (Swing) functionality # regardless of whether this functionally is actually used. # The workaround is to tell the JVM to run "headless" when # the monitor mode is not specified on the command line. set Graphics_Mode = -Djava.awt.headless=true foreach arg ($argv) if ("$arg" =~ -[Mm]*) then set Graphics_Mode = "" break endif end # Add the Conductor Native_Methods library location to the appropriate path. set OS = `uname -s` set PLATFORM = `uname -p` set JNI_DIR = $OS.$PLATFORM if (-d $PIRL_JAVA_HOME/PIRL/Conductor/JNI_DIR) then if ($PLATFORM == "powerpc") then if ($?DYLD_LIBRARY_PATH) then setenv DYLD_LIBRARY_PATH $PIRL_JAVA_HOME/PIRL/Conductor/${JNI_DIR}:$DYLD_LIBRARY_PATH else setenv DYLD_LIBRARY_PATH $PIRL_JAVA_HOME/PIRL/Conductor/${JNI_DIR} endif else if ($?LD_LIBRARY_PATH) then setenv LD_LIBRARY_PATH $PIRL_JAVA_HOME/PIRL/Conductor/${JNI_DIR}:$LD_LIBRARY_PATH else setenv LD_LIBRARY_PATH $PIRL_JAVA_HOME/PIRL/Conductor/${JNI_DIR} endif endif endif exec java $Graphics_Mode -cp $classpath PIRL.Conductor.Conductor1 $argv:q pirl-2.3.8/PIRL/Conductor/Processing_Event.java0000644000175000017500000000416511742733132021203 0ustar mathieumathieu/* Processing_Event PIRL CVS ID: Processing_Event.java,v 1.9 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.util.EventObject; /** A Processing_Event is used to notify a Conductor manager that a Conductor processing event occured.

@author Bradford Castalia - UA/PIRL @version 1.9 @see Processing_Changes */ public class Processing_Event extends EventObject { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Processing_Event (1.9 2012/04/16 06:04:10)"; /** The processing changes that have occured as a result of the event. */ public Processing_Changes Changes; /*============================================================================== Constructors */ /** Constructs a Processing_Event.

@param management The source Management object where the event occurred. This may be a Conductor a proxy - such as a Remote_Theater Messenger system - to a Conductor. @param changes The Conductor Processing_Changes that describes the new processing state. */ public Processing_Event ( Management management, Processing_Changes changes ) { super (management); Changes = changes; } } pirl-2.3.8/PIRL/Conductor/Icons/0000755000175000017500000000000012052546524016132 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Icons/success_medium.gif0000644000175000017500000000210411017055014021613 0ustar mathieumathieuGIF89a=H4o44c4bbmmmmxxxx YVaQ P gY `n sl,| u&{J9_ObSgYm`cWԵrsw w #} $~ %&&''~&{)*+-/(p1.336!7"5!8$:'=);(;(=)?+>+D0C0C1D1H4I6L9E4L:K9M;K:N>??;;BBGGIIKKDDMMRRTTSSPPKKVVYYQQ]]__``bbggjjhhjjllppsszzۂ܄ؐ੩潽!U,U996GFG60ECBA@ABCE0+D>;8KQK8;>D+&?:321Q123:?&U<4.--,PRP,.4</*('JRJ'(*/)##"HSH"#) % =O= % !$I$4bPaB 1x` `rF TIN8A`A,H@&5 \0@ 0p &U!@S*]JUE;pirl-2.3.8/PIRL/Conductor/Icons/Conductor_Splash.png0000644000175000017500000015327610663740214022126 0ustar mathieumathieuPNG  IHDRE pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_FIDATxwt?S/KKǥ"P*V@A=N w+W9E²4Q,ۧ%?2O&N2mg_dR$)Ye0 0 0L0 0 0,2aa 0 0LCPu[Ur.Iٖr-oΫ%m۔{L@n%9.eaN&0 0 w,1\k`sd(~@HN׸ݺ)C.oeyuZ͇Nb횎:>2 0L͇Laa&T+'3bb!gLw8ܬ8١?kJ&9:v @)}-iYMt Ž"May0 0 ĝjdV**r2E󭷔dpe8֯Sc)PR̥SJ< T~EǏ{;@iJ-$da; 0 0LܩNܷOq3*/xY5'(&hvu+e5-ΧB}OW))9mf0P6u|lsegaa!`aaM:Tvo@ b.g~?J7]Kԣȝ*V>@r?o-%}^t09D!hڵwR4^Y 0 Su:aa7TvyJuf(fwSJMI-7R'5@4s0uѤls"ޱMdajxaaxS)Nf83X;}1{'`cRسE٨XMSe9yg;E\r4QnL¬~d90 0xaaxSNfdyĸ+Xc4tНE$e'XMV7#V'S=ͫ*63RR=d+ٜ~|aad2 0 0q^3.1.]^ΪSy>hzNpfzsnrrX9!JgSt0i:PMr0׸L0 Üu0 0 {U.<޽#8ޏvhA;TIY<c?R1]q0C3Gt?q6}K1:䌒It30 sN&0 0 wdV׎>A:B3VGjLm't'*\e<}bܑcm[*Hc4#.Z_('s2 0g#d2 0 0q'.NfMq0*8ňk˔+]5R֟bR,渱Q( pY\)˯{0 ffǎuWTy8Ѭu2~y<|baZdaaN̳0>T('I3[sm}@ rmJglRl>T&2x;هa:aa7r2x*{SP3pDӾ;f; P&Nc8>< P R''.InmdUXbF]-K)`2 0v2aa::RQ+\G :~vmt׭pϭ8zi6>`{?70 0LLaa&ثEL٪Yl&b:|LWtKL1^]e>.Rb0s8SiL;^[Ǐ?ףu0缪d0 0aa7$GҌp\{:Dbd>@p}Kr:bR81޾[rW1*-Η> ]'3lH!ꋲ0 0e<aaٸQSUb5)ZrhJmޮ/ Su*"ϮF:Z/:>&zIt3 0 Èzaa7gi᥺jZ[dpϒ{w&ERt+ڹ 9``x6C8a:N&0 0 w* -.bP./\q=Z6@-*ss/r~!U#ov:u5Iv0a&uaa7N(Uqhc"u8:VSYB_dklw (Is>̩xQgqd>>s0 0LdaaNȘpeHqpwy:v u!G 4nsfNeEbA2৘KIw_1O> 0 )d2 0 0qɤX7ri9H>V9[bHu9d~BhS\^fuAmx+IN`rOrav2aa2&u)M2SlfS]Хߌ;T5bIؓ:c*/cJ30 Td2 0 0q'im,9"Lb]qf˩^*;BΞ8|2N99(쭷$o%0 0; 0 0L܉wYlf62R2hSSKv0^Vυ?Q:vUE[SafN&0 0 w"r2#442m,f7kjeu֍Ưrd`\](|'jzG^1ƴ]{GX̮|ca* v2aacfb31p˩6 ^s/gsשMmۦ}yеJǁQb.)8~>e{^1Z1ƴ];}?aW5;#)i{zC;h{ 0 odaa=Ś=iyg,rZeS}`eޱcszEL۾}pΥI1/ԋ$#Zs}.~Yrlrщc٣lg~@O mչdxz뷗Xqei:daLaa&+r:-gKvȱܱTzI-ZKq! 6#A۵[Y޶mL#HOw֭W>Ga?{'hĎ:4."{J9=9lgտ1n\ lÇ3{_d~m卺G}ҏt8 0 Td2 0 0q'&')rbu&#$Nzy@.u (H1I(:}O|rX{NVnJ9|OU0r,)QX/o6h1&emp=)O#4R<@Ka4EY^v;(&F>P3I߶mЍGpЪɺ^/8vSVsa"`'aa;r2@fu0#Vk,s):`*>+fSr )֓zL'E4lt)9PR?/ZGd9#-b,Oˬާ=HObգ 7݁:s1'a4daaN\b24vӬ'b6+b2sdٸTs߯Jv79.{&f̷߯,6˶YsetYsITWiCw/e+ 9>|4#sH$ֻnS?sg{uyʉ]qVBJUĺ9ԛ[0 S0 0 ĝ8fA6ºD~"{S%9[ޒߞ:ء8۷ok8S99 R$5Ƅ/ճ,/0* ܮ^Tr=1={dqK~XNq8E1{ΟH#v!crr<%4;XL7s!?~ e?^>1 0; 0 0Lܩ:^sb@elYK.sϿ][a۷5/.9.b')\9dpdj=k66i+.ݫmŀG AXErrwy}nG!^Nv& +ANS(o\vVbarBaa&c3pNd:tb9STm α˜W|sg5Sd2 0 0qRL t:6muʵbaAσcS۫w<ӧdWRpu2c$fS YU;"blf8lG?խtOu!F%AN(|@s[?Os_;=o֓aav2aac̅s, XLrDL=Sb!);x;2=T(Z$fS=ٵ#S_ Ƴ!c3ble.f8~|~ ⯏yR2lX!zq;>e;yri=#O;tr1=a v2aaceY셆i*`f;ou`_8^5 *ͮy+h{(:btKt;uj G͌i\FW\vt٢7q%WSΑ?|R7n)N>b5|oELr4il 8ubaʀLaa&ثburd̲gc(LXϒS;И8)yZb))[\\b 3ާXGTg%-y:#wڿls1˜):?; Rs%G9\ g=:P#ㆪqJu0۫=*gݦ)3'GqhQv0?<u^NVкukyv222УGk 0,2ajk׮Uڵk[V<F$\﯊֭[u,@aOf9뙲r}b׺ꦣfYɱJqzr.Y]GtCY9&F!8qT_sv%b r(<yٱTN@;;KfN~4VtN2|p=)vD@MO.LD1*>O}eǍp+s9jɊɵk?w5|VZGѣѣGddda 0INV6~6m*I5GrZɩba  0v2V\ŋ.' Na $):SbK}v(W^+@W(bZo3Gsf9|Ye˞jqQ' rʮ&^$9fJy$}̬Q1n?7m۶tuHLjG3//gŋq>V2ZСC1qDd? ĝŋ#;;mڴ/8x ^|Ei=zExP4-b69e#S\y;Mc+͜N0I(f9Vqoq3jz@U`VGqIdeeaѝbQNǏ;v 77ǏUV,X1 Sa'a,^SN1$(G*.ݸqc4nݺuӽ}vUxn߾%%%z<ldƝV1I1b2^ J*7s4b/lnNjIos'Q\s&999!v9ޢ8s͚GIεvm؉'r3l3,_Y69AɱRe߹kbĉ5b1|XVL,RWyѭ[7ynn.v؁۷ᆱb~dPJ Üu"a!??'NTd$iZaZatgyf$Ikڵ_ظq#6mڄ;vnlׯ_K/ŢEG,6"#3uM>eqH"x:9ɉXL۬Yn%CF#GssV.89O;NuciNYrrs2㏼%˔[>8_ܻWyFu$۵o7.?>,K6fU ~b>V,/^'6 vvv=HhҿϚ KQd\$袋Я_?\z5j'8ׯ_mԩSQn] l0LœQFaݺu5rIU2}E޽rp8 ˲*Yh2 ̚699Bd_55ow:)EG]n\tRC`I1TG^ݥ]~5ԁ' @I3rٲ]Arrm۔fV_Q[Hu1CXΧM_:TazKJ5jT{YE [3!okokEQZywnܹsٜ0amp:p81 "a}ݎI&oo]ibgϞҥ zsu9>aXdVkodHu&d;UAӅ\)ĀK9bHM .@v!=DqG س4q )XJr4?Hli3J8N9 `Ӧ(fʹn+W $d<\.\.ԇjҥKtR>(<鯑iv+v"So׋-Z`ҤI|a5i1f%eWPV/f&N* a~pMUx߸wJGu4ſlR]Y)Wl99b7թz{Սm9Cʻ/'u1]/`F)z4YM?zf=YV f&,3Ф?Q`\.U\jŃ;&~S+<FgM⺮#GT*E6 a ӬtbNl<$8V*:%IRK5y^8NUl\.ش׿瞋,x^gEfɩ IfNzqV+0=ew2y:>ɱb&mcJYIʟnBŎ:ĉPG_Kھ}sIC pN>yǟM.:b%p8;;I pT M1zx7sN8gJT(t: gϞx駱qrȑ#uFɵ "a|dggǥDQZZ^~eG;{JZIĦ==Hl\.O0wܘ\ͬ,t5hhT&ðȬ1%|c1b3 `PL?QQv#Gǝb+ r, r. O'f;XU뙓0bč/@b)vbLETt&e67vBՋhq'$Ww%^K.Azz9992e ,ZłMQ쉂ğfõ^:`֬Y͍j'N 4>ag8faj1gxY[7FRRHNNI׿f5T._BBߒ4"558s1{l/e 85ҹ{p8t]dI ].&sHYf-;u_-9bM*Ǧ1nE6;=/fɌ.KOySqsSbJiDgS\t4ERR,!-Y'L~ǝbaS~+#sĉx7={ =Nc?~vX4is1daNdk*NQoҫ<}m):'Ff N%=:lf,~;B;?Կ/gp=h<ѤiNK_3=E&uJ@Y+9J+?ѣGkiii2e : /g}ϖ5TlPpb6QVVCCx衇 bj& H0L0yyy.W#9 b2 SsEf2IpzJֲYlfPYLIʠHATu1C@dSݤΣ,K;yף [opϓsI^ԩ%`Ȼ 2s-F>ص@^|:)}:t* c/29 LÁ뮻ÇWon7,Y*bTkntݺuÜ9spBn *Y.&0gdFaĉ$OII>͛P 4$GOq)쉎qp4#^cZ̹4ۮ@/vWsƘ;u `~VGG$G^߀sٱJ+W,w̙3ѢE $bamժvrjsJbjԶ ]M;wZ=ł&05Nd2Lm'//F*<z!jJ  p8`Z^G퓭m=?pnz'vpV䉝E5R,VS($llJc2~'1~]_k1?cڴi1 .ޫVeee:u* LFw\:x<$ 6Mw]La0'6m>uM?}6M]nXԸLNaYYJ@l4N;xf=ixZʒqyjF̲EXǩwT{/Ot )0૕_ٳg|<99=RRRtncɒ%*.iT.&0gdB~~>N'NLlعs'|M`&"IR$쑘0 sVນeFLEs}o.%LH{=\=` :B}MT?'c*: r!| Tu7)6ov@f*ZϹ]'c&8p . O>$&u*FA 0gdk66jHM`:VH2L$BSێL2 ÜU"S/릱'GF#(:jos7sNdtס./qptHWTkkR^@!(Aѱ$2w7^ԩc^Y /$''#11,/KKKQVV ׫EjĢEh">0 SkD&Ü|ٖ}ZtUb'ENgv%%%|a;p[Ia#G ׿p,zY' 9MGfezbLFq+'AEZ,Lw0#M93}6V1R2Oy;e7jzLLLTNSGzvՇѹLn+0 Ü"a&|k˜3$f͝7ȲEG0lvPLq`GXwG +:_"u0i2ݫUӹb'Eq,܊>f3MN.sm&o8Ia S)I]}z-̟?aa+m>lL޾5}Yosьy,#w̲E28\mChֱ'Z2\O9Aa;({}N~رyWfeEQFȤn˖-30 Sp0Tm܍Yd{kb2 0UYy\m~hlC%{ls&Tŀh;q+bw"ͲU41^Nnh,U4s0)h{W4X1.vtR>1 0U; S ;|~cL%'0 T%guԿ5ЮRo1Ӝ@}̣)8;NX^'L # `3o3>X4_ >1U`ﮏsI. K,30 Se0Հ7:]wu/_`YXX0 Th>Oy&f՗:f ,[  dR'Sn`p,a8>ល:sUh۸;EYYY4,>d 8PWtz\. W 3| >S>2 0Od2Luf\A&t[nHIIAbb"V+^/PTTbrEԛ<7o0 nI_~ Z1S@ ѤΩ)b줘mN:b\zG70Gu26>lKԟINNƠA\Lt:p8`Z!2|><pzqܻa"a`w5nQ'~)Ot,h.f_Φ쑳IӁll16:0AN(a5NX/ݺs1S颬N-[V6"2+"3 0 v2 Xc颮[V"0m6+;TaZN)Ov`m.ֻ;xf1bchl? [ZXNZ<3DgUcN~rti}Jђ% 0;gLEjKS`srBYYG[=praE&Tbq2:i1na%wDrK>%%%>n[ow90 "3 m)܌wfpLбf΁ ui>WYOW 80ܾOˉ=6S9=K5R폵w3mZ6R1Me:[uFMcPͰ_ 8esv%3++Khy|V|Y-G#6/ $zJ$H $O?ٔa"aj+Gu2{mxw(*.CQIJp}J*t<La&2YQ!!:\ GX>9HȢDG Mfpַ\DyR1˜#Q~۷@ ;?!@]/3j=c|d|Rxbܻ&ߍ0sLGe9@I99xu"43ykQ\IvM+ϴۭp:*G3bN"ܻb֭[$~8i0 '0L5âxedH $%,%%*dՎ;RVZY㞎_5૯2ZbZa)V_^Ϗ1cǎ偬I"SI lci`Fz3{_tT5>M)1*[4W9bQ):~`㴅f/3opqo={B"QTۧ< GS~ ɘZɓ?z쉩SkQ'uw /Dii)V+l6l6vNh2ի-2&2~ @FFKG$`ڵQͣHNbv?iFt HhJ3ϙxyѤ+P8u.">Jf!t'd_NI+uc'ɉd}LipL}te];v\7󖛕;Ȍj?#. (Υƒ%Kzu͟{9ܹsZp8HHH,-sժ>g* y}HQڊ+`S6)jNrrd_9l}[2 2^ xtEP\\RvD*S ԩl|>unn|憌FYY\.|>Xbu$9b$E>'fG\[n3GQ:ݫn3.{:s-7N;YCri̝R[, qLs33D%uZRzuғ,0BpwF<}޽QZZVJl@aY9 <0p@Scj# %%%j,s5Td2 =[43iEwej=F%]k.Rr:m6;Uť^?wuJKKQXXӉDU`rY"2>Hc0u #a hR޴)tS)ٿb̡ڣ[JԽDJf2bXIr8)5Hbꧣb]OrR녆?eGX!_H6=Fx#yx!91k$xJtbInLU+2/^ ǣbecǎw9$)dR9Ȳ χKrP$,X/Zj"8NH ?&0Lxw>>=21lV,̃PVbίe{w9SD tP>_{f"I5kQZZ I`Xt:?g,opөfәtG14(';r4b4e\c3`R`O9XӼ~C7&^8>׬PV_V@h||?~F-CZ"S)>77֭- Ȗ-[e>}OƖ-[(quU;Ԙ*,ujvf1)4sի˵=d2Lpw{;#_7D5nmsr42F Дԋffaϧg;(\LӠ|ζüy>}WVPff&ƎcǖKi+3g]PN>^999Qh\֭SNE^Oɓ1o޼+''999A+2͛ch4KiҤشX,/bN<\[nE0ĿTY'sQowcӦMp°zy$2s»fBnn.fΜ7AN&T2 Ъyè?}ab,;$Sw:#M\s.5kf͚ڻwoddd܍'OƬY0i$;իmkӦ dYի{,ի k͸qŽA0p@ddd ??OW_}Qw1?7mT6y^ %.t}~F< gz޼y1 +I֙ Hl>'ddd@elٲw_XmIr zΓ%>_LM1n]bӸ9f 3QXcda=c';xJHdz_#Yw@fLwTmh^zaCW[l~okWE-[0nܸ'={yi/Zk֬O?E.0O?7x3fPHË#0hРJ? {̚5K'DW^ː֭[#33[VW_ꫯ6x{ވc)M뼙3_ʕ+h"E$UL]ȑ#&?,n˗g|>x^ݲhmڴ3t%8 m0nܸ }ʴ^?#O?᫯2+ڎG}TY,Ȳ7;Dt1ԆsBZZΝ:]n]UjMvT d2LУK˘>uA Ipf^(7oƍѴYYYp\j|Tw^9&LK:񓛛o?f̘qbرce)]>Z:_ÇǠATy)lٲ}N> Yu"ҭ[7-)f^<333UI Iq()) 6jƍ/㩧RχQF.6l0Ԯ=ܣ XAfrt*ih`lj]PZhms"eGlFcW)㏜/xzvGߑTb*gxr94{^tj)Ss)8jM/NG>լeʉC9luH=ЭqYq^x_X5Fq㢺m\ZZ J'GyO=&L &ĉ>}z|y:u hv9s&q//AzzzLۜ cƌA۶mΘ1?~'<$ Çxɺn+ڎ+ڋx1c&Myg1FQQ:)V{*2eYAnb? ~i_AÆ QRR&vȲ ݮgԩS#JJ:mvRʵƃCK8N1#Fc=9sGǃO?TCJJ*h\.tbjfyRR ݮ/6Mw_9'޽;NAa֭¢R$%:IJl&dj-ǡ'yp#` zi[ʅ:H9s֭ѣ.gΜQC8 @e:)AiN:;v,n6ԯ__)H}uVPhp\n+ EnAj/&M¸q n7 ց2e]؍G/,,ԉ[w;}@IF& }1;`XРAL:sx |uwI&p\vK:Z ۷׹.K!/tjoYV[n̙3k.\s5X~2JKKJ CZQ\EfYfu/Jɺm =d~#u bBȩ ?wsI <۪YgXw!S_28,3ғ-bVY :~hlּJc9vVDۋW>RrnH/pP+]HMYjiaaaz1ѺhmZ'n 7łu[p!1}t0ڤ :tG-cƌ1[oyZ(**P+}ݻubƍ{ t:QXXÁDbD|tMcǎţ>'ObΜ9x3`ʔ)D"^)oͪBQ(Y}ۺby$׋qa̘1x|8^L<[l$IM]RЧ'FJL~Gvv6,YVZXw [<( f%U״R{xq5l?' 0W^y%Ǝ>1%wsf`REfLmgzbU;vlwR 5;r׬=s):j;bt8BfN9~7xi1xUV/pm믿NTï(t)WR)蒚IѲY}8y^ WV7V5FӼ&.5kZnݺXlx R 6y0d]?bԩKѨQ#~6G]t$$$.O?:3o>51'7.t;pL͛7G/Zb$z#v333uB̙3ذaC '㏫U{ƽދYfŠ.RژFQdjQoӦMruϊNHO(Ztio XbE:'mO@(}ᇦm<8fBsUo矏͌$7}@ko?_}ܹ*L%I h)@8{ٳ'v_WPv$薳}O׫>S+EncΜ9AYyPqqi^a!ZqI9rDA-[Akڴ) }7o￯;~i̒zmx$(|Gq:Ԙ9fΥH_wV5}Qڞ"dsr_fY<\N]wC3믿_4H= @>B'"e@/sED^Szke]{V] C%[H j"InV8c̙ &9kVհ>xrw1XDg.L&hGV+V}?TǴlZ.$r(FL֎v>h 1jܸ8ƱcǢgϞ걯G{qCEG}(U1Ncd\cÝpbb*nv+ǵPTTT3\s*Bv?~[ZN%83toYe}S6qF-2""&Ž9vN >|94 ˿Kmݺuqwc֬Y7]_ " :u_~AYLqWƑE]t߱c#NH`EXȧb̙3!o5ek3χSNK. e|d1N ((VHo˲tup"OzBV|rdgg ˲;v&xc.D:ZGppε6_dSKcD\?8o5[,|Ǻs˜1cO׊s%\1:E!١C v2믿-Z(sRKNNƏhZaX1ضP}̵DӼ&.׶8p ^/|A;n[oF*Ec}-`ذaH#{mڴ JD/^+"r.I`"3ڬHvi9YY9-:X2#)b 4O/N &\/r1*uPY/m݆3gFb|ᇸ馛ွHżƗs ΚVdR<__a˓es΍(s8z(\.WЅQ'˲Z|:cM#|7;c:v~~^=sgsr_*bƲD" ޽{Sߏ1Z٣Gvun3s?qǺˍjj3k9AEmVfeCj9~Nfj˙T!%%)))ݏH.i,55EUnq)jJMNu)%!V4v"jэc! DGI4nSl=hJb ͉qfXкuk\yO}|A<$ &LQt V+6mUK(c,˺[uF2E . j,x۷Om%GBu]P, .b5؟VHjj*nVu]pݺqZ?X_u[{ {0mHM7n8=ZaÆOrnrZZ&LѣGǾѣ˹(4h ߂ z9!X`h|q SMQL7oWQ錻 ׿Bvj|>Y[lQ_Gr[l۶Mwe9d-/TJE|8uwa֭XtiZ{듨[Q2۷G̖'Xc=+|NZ'WVdj՞ϪnQqxꩧLB^@<\ve_-fXpӎ"ђoj-1o<ڵ "1Z,L4)xwC-[ -3##C]{\yhڴ)f|GLfZw}G1t´}#پݺuSɌٳO?ǥ} U۸q1bT'{VvNP*Mm"Q2X?Ғ\,2〘U>`qY/m⏭m:Y4M.v/Gt#.TR鳰_@-+1 ( ֭*8,˜z̋uN+Sdҗ;̙3 /طoVX[oUM'^{ k֬Qk[TkdiӼ&.׊uᮻѣ.(6m墋.矯HrhRSSbdܸqx<}(IذaEвeK{j+9@}hݳgO5 ѫW/],6+"{ *aÆ[fT>袋pyeZh{3// BϞ= ~v+ '˲1Ao^TY ,LdFE&T7&M_1w\ 2D-M'ɋ/{Fqq1QZZf)ʲ ĪѤyM]H_#??fҕ7zT.dmltAi۶m7o/1ݣGUd_ӧOoYZ(8} s΍O(' !CMlWJl[^ f&G?\(t\uŌؾ;M#MZ$ki=d [UR?GHx_GYBdI| t>T;zژ ~ zWWbXtqd\}W_ƾokx<)xRLדG={aϞCرcCntf޽Gw[+ Hdqh޼yԟ GM7ozx)"AՇQz6L|p*4Ij;kulC|.y{$͛.]য়~֭[cڵ .2Ǎݻwcػw/~hpڦMH^^^kǞ={gݻ{A~b^WQd^|سgv܉ݻwcϞ=!Ljζm۰a̛7O74oq v؁۷cΝصkWD7ۭ"wك]vlnZ+gΜqwc۶mwjlErhXʁo>fǎ 8@p6J"(Pu:L !LgGaN%k?/^=]*.(Nz}8ko<@T/s?VUu]}?e˖53iTBsu;qURnӼ&.oX@q pB<={6v b|cl2|=si|7lLy睺",G] ۷onj3tdddnPoΛ7… w/ڶ/2F:1 meB,Xd >#GVyaРA8t^|E\3.~EP/.]|[[ ?cYd2L-p3%%}Ѡ[:e̟?&LPťj$IXp!N>(}|\|۶m5b|۴i'b sѣq뭷Qֵ=sLD,{W_{e~1_#qΜ9sQnQ3FA>b,Z(D$"Jź}H?<>| ,ĉqFTܼ*XD{av2kv8fݺpb}86CGbo3>Ht;wp+tϩO~>St0ENU WΜQT:P'mh*):T/k~TBn>|xLsW\:nv_}r+~aUc^z%t˗/WoM6 ;wr!ʙYLO? ,x =j  ܹ30`vZiӰl28z:O?4,^mb Ņ =-矏ȭ 'eYƛolbUW]N::ܹ3Z/ڛ{h޼9ƌ,ugy&ӧOnݺa3yc$Y[ d2L5N(ԩ@Ev}>矏 . bɌ||jmRڄ+w׮];$''Z3rB9ǎ Yb652ıi9Gys?|u,*▬vlm86N +H#Fp˖-8îYV1!de ;S}mڴi矏Ν;7WygXt.,P^̓_7r֛ӱY F'**$r U>p1:hkI:6;>t.{'N_Q9t'{Jw'Pd9h45L#9UU/N|T~x3yݻ=&OKl/۷I'^za̙?~zQyꩧpQ~IO6 {Bɓ'c޼yҦM5d`ɒ%8p fϞ]vC%33=n\j9A$D7 sǏg}͛7G=W^yuBAQqqqLv;</\_|~aûヒ͛7_~u]+6mXp8M" EB[K. ǎC˖-#μMOODž^T4ݔ$ b.'NDnn.y_|#G`ԩXp!6n܈7x#*?0cIa _]t EsQر#zvکݽ{7>䓐}eYVch#ǻヒ#Fe^x5s{ժUj(ݻ~EìY0k,]yaҤI ~j ə3g}7o{=,\0QO~~>|M<38}4fΜ:'ֶs1o"b0XfwMiXRt::>OYfP6u7DƵm:Gsʔ)nmRGg/:}V8Li?tRK]ANipݴ=egϞ!C07}:v쨞P- 233jժF'ƵXhnɑ#G ͛7ǝwމ;w}7N>MxЧOnխ[bTBŋ_ɲ ۭ>˓q8(ϟɤ^/n7\.P g}>˖-ڵ 7t׿bȐ!x7plذO3,2m6,].\P]7aX,fc\b֫V%%%m۶wލw}_84hK(馛0tPu^:wު}qwc:t(8?gΜQl$%U0G}4#7@~~>Μ91c`̘16lnGΝ;qU05n7։lYcRZ,5f(4(M4Q/6$&^}UL0VMCt:ѣG,X˖-ӕs9rOӧwٳ'ڷo:u[nO?Qcʔ)HKKS/zv HJJBbbzA{ꩧb:sL5Ob &={O?b\&5k5kKxc ЬY3\}p\|(**5k%3dff۱~zn:m1cO"ZSOa͚5ܹ3Բ9qg5 ͺ.n0vX$$$Nuh'v 4j @IZ`.rt}A6mt-([/DNNNu4hǣNZ,dddZ磠gٳ1x`{ҥ nF$''r?{aؿ?lb|jEp8j9Ak0Tms j8] F"si @g_74Y̥)fGZ3~R@R s?s^MTAU7r3Ǐ/}۳gW_չ:$oƖ-[}vͯcǎ%6T1%CcP~}{ク˱h"mٲvl\p3f .6V&$$ 11QwAK¾}p x7-/^Ľ^/ƏΝ;}/ZIЫWr'~Y| 曘ռys<{p8V>ˣ94h&MB߾}`]MUVaժ@ܦMsX8zhPlÆ Ø1cп|>*il6Ȳ={/C=X Ls=3fRWw˪,2NE]S?+V ##fROuԩ())AiiDxbۖʹjf'mgVFªUѣ1-N:2d̯=k/w-fZҐ#E9 ǎ hc.˲XKзo_,_~ F¤IЮ];Ӭh?DtR<#X|9+lݺ5w}!CлwocM(t>)χ/}ƍ_Ldff& ѣGe˖x<())S0ǞPn˖-âEtҘСCqcذac˥{}p8tg9_Ņ^hx+Wt)Z |8av=n|; EZ^3Ps~LeLIP%g㏜Wn܂Y0`@oH΢BII V\Y?m {}}@@ǐvм)NPLJjc`i9tkyHc~8Ɩ=!!AJR1z6huF8d -^:\s5ظq#z뭘׿wyzk3M'iLt: Z,݅KK.ړbZ& 1|DBhDAtЮv?iѭ1x-ChC_떚vgbMzиgH#&#U#vy_GE!(koh Ihh_Pϋ>NHǽ5: ]@m"$\VlvuFKǵl  PNzNnxNEf\,R~pKt0*W9$zS):'gr (u,dH'iӦab?-[";;[=%$$:֚AotEVVi/&ZC{QƄnaIqb9iFű!݋Kbڴivi.tqԎ Zi$$[ Kq`$1 XGG(uK+P~?}lֹWTK@+Z$֘LpӺb:S3:ǽvi_ުO]PMm\vԖsv q;rvyCIhg8̜9"߷*u0 ^;{ǹHեO,h3c)K.œO>YyK;u =qתݓ]HC9Mg䠘5*L#%mNkD.$j8y$ piiiX~=Ӄn+eja䜊Η6)BLBjvޑЌfzi(I mb?} Ff?}; E{`$Ǽ8Xs^uym?'pv9RGS{M7݄͛7㣏>y_p<A'FaӦMxGk6.Nkv u h$1FN$]@ǎUn:p 8sLH7ĉ # M;j}đ.q.юxQV2͎Q9[ёLXth{vݞ6ubXi[9.L+ס6%YP!$z,2Ylsq9]7@9͜2IeP3ͨ64iR&r-?v]0aӶm54c#), lNBѬEb GZZ[ԫWOF F \07;6u'9vfDŽ˼F"Q/pkԍ(qXLP|Ng9; SC]7m:̜9,?y˖-Î;矣AAu Z =64ZVE{7*z1zh[.ǴitnQL[*v;7HHe 8.׆T&t{sV$ﱮOy뺰YcvJ Lb$z9ι$Bx9|#P&cwܡ엵k('J:;.n;F_\OOOg}={%M0\.,X9sĔ^̳+WbԨQ!ob͚5h׮g0g|F`d&&&")) 5… Ѯ]rͻË/h(2nV;ٳ'@~~>62?C駟ʽ :hD\.Y Jv2óxbL8\R:f7a!`+QȾLHH@RRhΟ?W^ye's~T)Ȯݥ[_߰7*Nu㭷;v 7n|Pnw:yyy:u*|*]fRxvffN&ÜmNS;L2dHܖ[PP'x>>$''#55U}J͛7Ǎ7ވEᥗ^u]FkbСhӦM L5kVMea;53b1}, _~:y7zgc}HMmMr5ʰrJ̙3'Y-Z#v֨Q#ٝ'9#GTӾzo3L-Ys0U."Z{nNZ9 عsX/,rtQj_Vy$h;y^]zPEU81h 4nXjԊQ)fNfZZZp2vj/, .ޫK΢*Ҕ>--M-ap8`ZYd2L-LM*ΥI1t6 w}7."̙3۷ouڹs'v܉iӦK.},nj-5Q,gr*r*#*Faȑ4hԖҶ22YT/L0ץo )񑝝/XMFw## i&lڴI}sܹ39M4Aƍ!Ip9kڵk ʼj ^^qI8!L Og^kdbEt:q\xA=E:%M,on`qC&=x<NJ+*MlFBN.] ##[F۶mp8жm[dffd!//T?ϫs,ey% KݮOHHPE&==& S;a'ajLMVHh]Lv. 6 F 7܀+V>rgbuͼSM6BKr% Ld2 c韽LaȀ,C YH,!I.H,{ >x8*<%|RBzzwo0))F1[+,aꄕ',Vu;,)Xy1 S`'a Y+,+$d,{A7AqԷ8qb5/$5- FURC6};pxwp!99]vE߾}ѵkW]/q3aiVlģ(.lm=^% >" Xf ,YR!+a#55vGwxސ+Sx՗_V`u ᄏǏ1tO?ARR2iL# 8dž $6CC;2:G_,E^^6l$$iuѾeeжcN ~еkWoFR[D]+.IP Kv.YȲ{nZ!I?8+J>Er$0F,łؑ0?EܵbݎNlͅoIPE*6}^w7%I,PE;kF|euطہG QFhӦ v w.2mh\ Km!u x({6HZ7Rcj"-R-5ﮱشR8 TM!B N9i'p3++ 5BƍLDWۍ(֒" GԊM#aaFdFe۶xi[;Eh8?ao3 `f8iZѽekay!~zS |Jf]7hgSwi"8@Rl߾cǎj#$dVV7nlK {Fnhm0K؍s\vvcڰȬI ЮYϧ-`OVEѺ;෵ O41L1V6$@VlZVȲ~l8XbE<4%Oh1>)|>Iu9)"HHNE]Ѵ}g!U{kǏkRRRtmڴAJJJшI3aFIdWkkʒ2dH >^XCZ>O$0ſ8NXJ#a6Ci$*YXFG}y׶q۰ksր |> W(s{p2N*97M'|z >F|" ϸec(HTdD 2Xӂ$F~XZR:tȰ Q"^yd@aEFG YjӹP3B%FCMc&\YTƟ+="F"abEi4J(V XL LK ?"bЕqD+2 5ɉ"L m &FM(**BQQvWf^E@)+32qƭ LHev>ژjO*nj˲2m,2svV6+ jTn[`~:!a) *|dvv;-J.>92&Ȋ*1fV+8Q6^zA @m=n&"3].$ ²9ibShۥUI,:g&t:ѯgkzVM)@=jc=AL@zr"nzq hтR±Z찚pj.leOJxu2PUiO ֵvo҆E{X4g:H'}"OQi+X2LT"3Y {TR'jH /iV?zj)RnHEj N^RRd)ul3N\>}۶E^SE(--eT .PѮNl]<PQJJi"/PD?Y&dl +1]u5S?JS~[]6JocH#2lݪKK? Z LaddUD );nh^5Vdf$: !>o1|^Fuд2Z4>lw}'Da0WЩsg>BJcNdʼX$X bUD*V"P݂;P6OT[qxqX/z"tK@󼼣 ڋ ?uw[+@ 0QLHMU;r1{p:7AbMh*P`YkT2+frEMxކ2? vӾeTZ386*0HqXW(co>yGj d=l%[ ٮyϡh`z{[d)%_B^JJ>}.-- r1z;To"a"AJbtnǙ|v}`Ki'Y l$Μ)#G //;vߎU[_PE@zLx;K7_2l)O}nP7% Cd'R(*,@љ"YD_j/trS82dnhETE@:>(^ %El2LEs^HJJRi.^Gú f&2gXd`8PR* IC)pKZw2:糡$'8Y')8y$ ZM|8o58|pZzt/_rqQ'%6]iY~kN_U_=et#=щD@RRsuyELU1wĂ["%d$@ @B^ M+ 5Z&NRZt2}uC*׾u3^_ ̈-E8[!F״ K](9>x2D8v`$&$$ 99YY]qect<늠Iㆸl/ê !-][6/%M7B4J \Ĝ'6n?>+V SѼAiZO'.M y:خ5ۗO7dU6@~+ xe7PȈI\چnV_)MꕔK$ H~ YN'i &4yLJJ EfD"tFiV$%&"5- Ie^v u22PPPdԩ"bB$eeeh֬N'222ШQ#1) M_MzGj GƊ"̃ahYϻVr!s:h*0ɽt8 =]yGr;]FJn?ZnXka*3nc_zZ<ŢZ<2vR(a ,--okӺG/B^ߗE&"3$ɇ&^zy}((,FQQ ^7J KQNX-nj"1) .r Cbb@v6 ׊{Y#ŵl-LA6~U3Oǻa9)hؠR‘$0SHMNCr4FRR*T$')WR HEEH /1=ځG~>)ln%~%g\J[JiDct\9̓lтvpРAbdQQl6PVV؉xh֬SR``;jn.KN-99M7T]EиqCtG+?Cy&ED hDzSwqdׅ#*2)9U|bb(+S(>̖qI^XjL''dAv+`6 l;mJrvk{.&ĂX3st"aXd`X38_"|>|>xLJJbAQQ!RSӐ Nj"ժLHHP3}>222?"$ԫ[-ZE˃3E2>#сE l)F,":mԫ'#)!4TnLLL}?QTXS'Od\ܵz |\'X-KY*ȝV nAfO'LkX,JOO) ȩ X3ƫ% L6Nu6 >>%vxiZ C/l~ $0qrݥn) ~xƭDe .IwOiAۙLdoH+6omLNbXdFEAA>|>N'JKKak/Ȏ;"==]V N" ˰mHHHUTHr{$''oDqq14·6mڄ'Davѣm돺L%^sՃ,˰Ȧ.Bz=+vRzh٠)F=,-6m$IÇ ?lGWؒe)VKJK(c#-=TTX'U$@7lo3ļ^Q&9<R/Yct^zsPYFڵ @^z)~'{vU`)neRRJKK{n4hhhIII{zШQCppklu22 99M2X}Dw !DHw0~6oތN:!==]lhѢ%Q\ҋ%(,TbMQh9uuG&J  E((I//L9;83f_)DQ"մ$;,KMD1q3x2" gٓ" 0 g v9X`3Hݼ&Lf؞ĎV|QteSԅxkٗ}(buI6%ʖXS;}|AG ʮ$%4׸cM'#I`6FA"7lI~le͠E LDOu6촫jn675,`]bRI#.gW$Jl6K"PX_?vx6_twHdL|p:[8q0T.\˰n]նex7Y^^bرc[ITNd,P]BۭͅG0QL`#Q*kJ%(l+qС1Fc7ؕUTCpuG3ԣ΋ZgeFEyv n%usIըs ?{b{ϩ՗_ܲ&$S\at:z!ɤM2LeNzsܻfIb \݀ɩ/[Tѫ܉SM'z'Siff3'e"|Q́͢ Kc1[X+B.I 5b .drU\vR"rInZ3dǮ6Hd2XM4^>SކZ1=Lf&$Q8=lf}}*@\.G͹>}|˪̾6xL(ɎKeNZ?v|੧T.̦9~ݬr/v?nsDS|Yht2.w]]][+`ɵmd"[`$2њ66B\"W0Q̯h}2R;hV5bWS"UT/w,T"-JjbG|kW ?Mt7$[P.1LlN~Nv;&I_uPٌfaaJDb-EOO7]-x:>kfpsYXK&y8D/LE</37nsğS yy?f6Ir m[HzEقfv3_\&M.&tns6&fxn׾c%QPZava3hb899*VTPٝ Mekqr#?kUn׫6?iM g4UUEX,&l6;V<JT*^Q*\N2t:mZ̵\\zF#.olկj"Oݼgp$p:^:ş^{og~~AO1i#qLor+3iVS&]8Vub{'iuuseiK3skaϛxG?xX8 U⋝2:n/p]fZL̛ my?mY}q>ߗ ?{7l4u?κa?;>:sE25rid2i{%===X,f2RRuɲL.?z(]݇p84ioen2w{󛃌RQ(+v0s=\*k`yXIV(q\XGKOg=cS.2?P>ʩMBv I9<+d i2Hfziuu1V1;9_~HIQX(mmm2>>($ >XB!BЧ&0 31500p-/tYjxKߟ0OOZRawe̯)J*Z%m]vV~1(`YAFbO$7ccgk{H$R՗wx26;d.ZE10^B&giZXTsgnI&Ӭ>{3tAvf,&.Ɠt ?{N%ڍ5fe**<[V8ȗ [If u-!I2M^PUEe";YIyr9rY#| }|wm[/`@ 0`PH$0ht:F"`rrp8o@:dttOMEY-k蹁@`_E-$PEfI SdlAڱ8/-^Ne6n8x+iϪ\N({-yK{lw͌B%E^yz]_,h`|^?D|sާ3}}F hk;bbb׻ijrrX,F<ߵ?#J2kťrL&GӲcB#X,fdYDw&<20R OBZ58D\$$E.j.{C{]|TRVH@|@Z\c6yZs@ZBIT,ϕr:?Ci#Ӻ2zzz@ t |>b~O~(`h4cddAJ)H$DQb###|>@@W(|""b.r?u;^'$o1x<^UFLA(j}X yCn&Y΋ss\uaD@|VFZXQs{%Z$nfU>XQTɖTeL,Ung+62 PvQϢıԓFHjm"599YY'%^@agxাOϰ:}nEGչw^~3Gt2$3o}^^=p MD)VA/'''g6~4###DQ"@@3B&\ N$:Eڮ0>>^E"Aloqut}LIQUUY(4 a\ H>%X,d(rx^NRmt5>'k$3sNNghsq݁,IHDOOO\Q385WpkW<7Zܼy'Nh'K$e9֪qeiTM&Sak\,fc25fn-/J1@7X,F$٢ȅk 388[& |>FGG  8+ە$2 WP  D`tt讒)bdbbH$ڨČ3X,~A ƈFq}5^ 799ԔrO|B!; -9H7xꀌY\q7KZI#~k9q8Vu'2^V\ZEI\MvɇJWAZvr"\һm< 85J~ɹ (ccc[&Q/?'r~Ȱۻ.~16TZbWAu$szzi0DBןzAt$vzx|gddD'h/?X,F4<44Ą~vS`0 cccUFQΟ?EQFDphhʛ&144緭ӝjiM&f4ͺeo}}ӹ9<.XT8u_"mhI#lb8{7B8Np,k;K=9rׯWĆU2W6D =Wƍ:ɬE{NPm+mXH&_X̑kK>[|l-pu#DԔxyF|pR&x67~=CzA"=7j#,,;cdI2ɄPmK3---JZJEْAHjfs 4e~>9 º}v:Her~;J97$*p!n͝ Dd^#$ɏ/"+mWXճb^^f!g=aq2o6a)Wd N>*"֎FBpX__c>&ly###o0::̖w'*7茖OMM144Ϣ/fϟg||\^'$v" {yw,׶w˜J xdTU#|LJE>J0Z Eڭ_ONN6TP ɿSDv/xΰQv!u~wnk XJW]W),{ћbIQn[ѯ]+M֒dQXekT%&Ƶbqp?l7IaH$T*rvreTUٗu"e]|>5U~=!*O8ȭ" m m#EmPE,[gGsRB'ʮ0KVHͼrRݵF|TL\av)LfVCbzVmj8 4/8Љ]v aY0.bpt pB!]-^9@LU|O0dhh~?HD_dtWz`jpbbBQ~3:}>_D@|PH'❢|ƲV]K.YnD"AA-&x-s'wi'ߍ iE?ic Dv Qvjߊmp8L(bppPo sFʹqFpgsspH$˲≺6Ev.Qo Kh(^ĚHt`-D iFfjjJ )sLUR=<^6Tj 5*J|PtvvN) *3o6ǏNYJ E EV -Lf@::tO9wU) W 7]::~:iQm\3~xe3}>'2P13ߋj^>Dג +eTfF&>%oxKKLIyyW(˘f,KU,բ|g\)c6iDR$hz8aX68N裳FbQmXY-mmmtwwqm;m[(2*WM {9ܹt$.OBa=XOTfUWGPDreMWw+ʪMb# M<K*f &}4.b,//ҲI EE9c6yW{F|%b̑(*x5$^`qqo|^CJ$*WT.)gU'^t7DM<:>IENDB`pirl-2.3.8/PIRL/Conductor/Icons/warning_medium.gif0000644000175000017500000000115611017055015021617 0ustar mathieumathieuGIF89aLK NM3uu3WVV6VV@ON3.-!#+.1FP^l$**+,,>lq%&'()+1777Ukʧطظغ#$&+,'(.1ؼ*34474>@>Aؿ'  '>@B,/C<9$ $9. *******************************************************************************/ package PIRL.Conductor; import java.util.Vector; import java.util.List; import java.util.Iterator; import java.util.Enumeration; import java.io.*; import java.text.ParseException; import java.lang.IllegalArgumentException; // JCM package for expression parser/evaluator. import edu.hws.jcm.data.*; import PIRL.Database.Database; import PIRL.Database.Database_Exception; import PIRL.Configuration.Configuration; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.Selector; import PIRL.PVL.Selection; import PIRL.PVL.PVL_Exception; /** A Reference_Resolver resolves embedded references in text strings.

A Reference_Resolver recognizes two kinds of references: database field references and parameter source references. All references are enclosed in curly brace ('{' and '}') characters. A leading dollar sign ('$') character indicates a parameter source reference; otherwise it's a database field reference. All field references are resolved against a specified Database, and all parameter references are resolved against a specified list of Parmerters; if no Parameter list is provided the Configuration Parameters from the Database will be used.

The resolved value of a reference replaces the reference in the string being resolved. Replacement occurs depth-wise: the most deeply nested references are resolved first. The substitution of a resolved reference value in a nested reference modifies the reference accordingly. Thus a reference may be constructed from the values of other references.

A field reference is a token that produces a database query: the query is submitted to the Reference_Resolver's Database and then replaced by the first result. Field references have the following form:

{Catalog.Table.Field:Key}

The components preceding the colon form a fully qualified field name, and the component following the colon is a match (SQL WHERE) criteria. Data are retrieved from the field only when they match the criteria. If more than one result is returned, only the first is used.

A parameter reference is resolved against the Parameter source list. The parameter reference is replaced with the value of the Parameter having the reference name. Parameter references have the following form:

${Pathname@Sequence[Index]...}

The Pathname is a simple, relative or absolute Parameter pathname used to {@link PIRL.PVL.Parameter#Find(String) Find} the parameter in the Database Configuration. The Sequence, with its preceeding "at" ('@') delimiter , is optional; if present it indicates which of a multiple sequence parameters that match the Pathname to select (the first parameter is Sequence 0). The Index, with its enclosing square bracket ('[' and ']') characters, is also optional; if present it indicates which element of an Array Value to select (the first element is Index 0). Multiple Array Value element specifiers may be provided to select from multidimensional Array Values. Both the Sequence and Index specifiers may contain nested references. They may also be mathematical expressions that evaluate to any numeric value, which is truncated to an integer for selecting Array Value elements.

References that can not be resolved are replaced with the {@link #Default_Value Default_Value}. However, if the default is null, then an Unresolved_Reference will be thrown.

Any escaped character - a character preceeded by a backslash ('\') character - is not interpreted with any special meaning. Both the backslash and the special character are left in place.

@author Bradford Castalia, Christian Schaller - UA/PIRL @version 1.47 @see PIRL.Database @see PIRL.Configuration */ public class Reference_Resolver { /** Class name and version identification. */ public static final String ID = "PIRL.Conductor.Reference_Resolver (1.47 2012/04/16 06:04:10)"; /** The delimiter of database field reference components.

N.B.: This is not necessarily the same as the {@link Database#Table_Reference_Component_Delimiter() Database table reference component delimiter}. */ public static final char COMPONENTS_DELIMITER = '.'; /** The marker preceeding a key specification of a database field reference. */ public static final char KEY_MARKER = ':'; /** The marker preceeding an enclosed parameter reference. */ public static final char PARAMETER_REFERENCE_MARKER = '$'; /** The marker that starts an enclosed reference. */ public static final char REFERENCE_START_MARKER = '{'; /** The marker that ends an enclosed reference. */ public static final char REFERENCE_END_MARKER = '}'; /** The marker that preceeds a parameter reference sequence number. */ public static final char PARAMETER_SEQUENCE_MARKER = '@'; /** The marker that starts a parameter reference array value index number. */ public static final char ARRAY_ELEMENT_START_MARKER = '['; /** The marker that ends a parameter reference array value index number. */ public static final char ARRAY_ELEMENT_END_MARKER = ']'; /** The marker that escapes any special interpretation of the following character. */ public static final char ESCAPE_MARKER = '\\'; /** Alternative source parameter reference token character. */ public static final char ALTERNATIVE_MARKER = '|'; /* Programmer's note: Other kinds of references could be recognized. For example, perhaps a URL reference of the form "#{}" might be useful. It is helpful to think of references as placeholders for information, which is retrieved from another source in the process of resolving the reference. This definition is similar to that of a URL. */ // Pattern string being resolved. private String Pattern = null; private int Pattern_Index; // Resolved string with references being replaced by resolved values. private StringBuffer Resolved = null; private int Resolved_Index; // Database to use for resolving field references. private Database The_Database = null; // Aggregate to use for resolving parameter references. private Parameter The_Parameters = null; // Case sensitive parameter name matching. public static final int CASE_SENSITIVE = Selector.SPECIFIC; // Case insensitive parameter name matching. public static final int CASE_INSENSITIVE = Selector.ANY; // Pattern (regex) parameter name matching. public static final int PATTERN_MATCH = Selector.PATTERN_MATCH; // The comparison mode to use when finding parameter names. private int Match_Mode = CASE_SENSITIVE; // The default when a reference can not be resolved (null := throw). private String Default_Value = null; // Counter to keep track of depth in a nested reference. private int Level_Counter; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_RESOLVE = 1 << 1, DEBUG_PARSE = 1 << 2, DEBUG_FIELD_REFERENCE = 1 << 3, DEBUG_DATABASE = 1 << 4, DEBUG_PARAMETER_REFERENCE = 1 << 5, DEBUG_ARRAY_REFERENCE = 1 << 6, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Reference_Resolver to use a Database.

@param database The Database to use when resolving embedded references. */ public Reference_Resolver ( Database database ) { if((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Reference_Resolver"); Database (database); } /** Constructs an empty Reference_Resolver. Until a {@link #Database(Database) Database} is assigned this Reference_Resolver will not be able to {@link #Resolve Resolve} references to their values. */ public Reference_Resolver () {} /*============================================================================== Accessors */ /** Sets the Database to use when resolving database references.

If no Parameters have been specified, then the Database Configuration parameters will be used to supply them.

@param database The Database to use. @return This Reference_Resolver. @see Database#Configuration() */ public Reference_Resolver Database ( Database database ) { The_Database = database; if (The_Parameters == null) Parameters (The_Database.Configuration ()); return this; } /** Gets the Database that will be used when resolving references.

@return The current Database. */ public Database Database () {return The_Database;} /** Sets the Parameters to use when resolving parameter references.

Typically an Aggregate is provided for use. Otherwise an Aggregate is created to contain the Parameter that is provided.

@param parameters The Parameters to use. If null an empty Aggregate will be used. @return This Reference_Resolver. */ public Reference_Resolver Parameters ( Parameter parameters ) { if (parameters == null) The_Parameters = new Parameter () .Classification (Parameter.GROUP); else if (! parameters.Is_Aggregate ()) { The_Parameters = new Parameter () .Classification (Parameter.GROUP); try {The_Parameters.Add (parameters);} catch (PVL_Exception exception) {/* Shouldn't happen */} } else The_Parameters = parameters; return this; } /** Gets the Parameters that will be used when resolving parameter references.

@return The current Parameter Aggregate. This may be null if */ public Parameter Parameters () {return The_Parameters;} /** Sets the parameter match mode.

{@link #CASE_SENSITIVE}
A case sensitive comparsion of the parameter name with the reference name will be made.
{@link #CASE_INSENSITIVE}
A case insensitive comparison of the parameter name with the reference name will be made.
{@link #PATTERN_MATCH}
The reference name will be treated as a regular expression (regex) pattern for a match with the parameter name.

The default match mode is CASE_SENSITIVE.

@param match_mode One of the CASE_SENSITIVE, CASE_INSENSITIVE, or PATTERN_MATCH values. @return This Reference_Resolver. @throws IllegalArgumentException If the match mode is not a valid value. @see Selection */ public Reference_Resolver Match_Mode ( int match_mode ) throws IllegalArgumentException { if (match_mode == CASE_SENSITIVE || match_mode == CASE_INSENSITIVE || match_mode == PATTERN_MATCH) Match_Mode = match_mode; else throw new IllegalArgumentException (ID + '\n' + "Not a valid Match Mode value - " + match_mode); return this; } /** Gets the parameter match mode.

@return the current parameter match mode code. @see #Match_Mode(int) */ public int Match_Mode () {return Match_Mode;} /** Sets the default to use when a reference can not be resolved. @param default_value The String to use as the default. If this is null an Unresolved_Reference will be thrown when a reference can not be resolved. @return This Reference_Resolver. */ public Reference_Resolver Default_Value ( String default_value ) { Default_Value = default_value; return this; } /** Gets the default that will be used when a reference can not be resolved.

@return The String to use as the default. This is null if Unresolved_Reference will be thrown when a reference can not be resolved. */ public String Default_Value () {return Default_Value;} /** Gets the last pattern String that was resolved.

@return The last pattern String. This will be null if no previous pattern String was {@link #Resolve(String) Resolve}d. */ public String Pattern () {return Pattern;} /** Gets the last resolved String.

Note: If the last {@link #Pattern} did not fully resolve due to some problem, then the String returned will be the partially resolved result.

@return The last resolved String. This will be null if no previous pattern String was {@link #Resolve(String) Resolve}d. */ public String Resolved () { if (Resolved != null) return Resolved.toString (); return null; } /*============================================================================== Resolvers */ /** Resolve references in a pattern String.

@param pattern The pattern String potentially containing embedded references. @return The pattern String with its references resolved. @throws ParseException if a reference is not correctly formed. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. */ public String Resolve ( String pattern ) throws ParseException, Database_Exception, Unresolved_Reference { // Ensures that references will be completely resolved to a value. Level_Counter = 1; return Resolution (pattern); } /** Resolve references in a pattern String down to their end references.

@param pattern The pattern String potentially containing embedded references. @return The pattern String with its references resolved to end references. @throws ParseException if a reference is not correctly formed. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. */ public String Resolve_to_End_Reference ( String pattern ) throws ParseException, Database_Exception, Unresolved_Reference { // Prevents the final resolution of a reference to its value. Level_Counter = 0; return Resolution (pattern); } /** Provides the resolution of a pattern.

The pattern String is copied to a Resolved StringBuffer where references will be substituted with their resolved values.

@param pattern The Pattern to be subject to resolution. @throws ParseException if the pattern contains a syntax error. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. */ private String Resolution ( String pattern ) throws ParseException, Database_Exception, Unresolved_Reference { if (pattern == null) pattern = ""; Pattern = pattern; Resolved = new StringBuffer (Pattern); Resolved_Index = Pattern_Index = 0; if ((DEBUG & DEBUG_RESOLVE) != 0) System.out.println (">>> Resolve_References:\n" +" " + pattern.length () + " characters in " + pattern); // Parse the pattern. while (Resolved_Index < Resolved.length ()) { Parse (); if ((DEBUG & DEBUG_RESOLVE) != 0) System.out.println (" Resolve_References: Parse completed up to " + Pattern_Index + '/' + Resolved_Index + '\n' + " Resolved: " + Resolved); } if ((DEBUG & DEBUG_RESOLVE) != 0) System.out.println ("<<< Resolve_References\n" +" Resolved: " + Resolved); return Resolved.toString (); } /** The pattern parser.

Parsing is applied recursively on each reference that is encountered. A reference that does not contain any nested references is sent to the {@link #Resolve_Field_Reference(String) Resolve_Field_Reference} or {@link #Resolve_Parameter_Reference(String) Resolve_Parameter_Reference} resolver method.

References are substituted into the Resolved StringBuffer with their resolved values when tbey contain no nested references.

The parser keeps the Pattern_Index location and the Resolved_Index location synchronized to the same logical position. The parser also keeps track of the nesting level.

@throws ParseException if the pattern contains a syntax error. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. */ private void Parse () throws ParseException, Database_Exception, Unresolved_Reference { if((DEBUG & DEBUG_PARSE) != 0) System.out.println (">>> Parse: from " + Pattern_Index + '/' + Resolved_Index + '\n' + Resolved.length () + "> " + Resolved); boolean is_parameter_reference = false; int pattern_start = -1, // No reference. resolved_start = -1; if (Resolved.charAt (Resolved_Index) == REFERENCE_START_MARKER) { if ((DEBUG & DEBUG_PARSE) != 0) System.out.println ("*** Field reference start at " + Pattern_Index + '/' + Resolved_Index +" - " + Resolved.substring (Resolved_Index) + '\n' +" Drop '" + REFERENCE_START_MARKER + "' at " + Pattern_Index + '/' + Resolved_Index); // Increment Level_Counter. Level_Counter++; // Drop the opening marker. Resolved.deleteCharAt (Resolved_Index); resolved_start = Resolved_Index; pattern_start = Pattern_Index++; } else if ((Resolved.charAt (Resolved_Index) == PARAMETER_REFERENCE_MARKER) && (Resolved.charAt (Resolved_Index + 1) == REFERENCE_START_MARKER)) { if ((DEBUG & DEBUG_PARSE) != 0) System.out.println ("*** Parameter reference start at " + Pattern_Index + '/' + Resolved_Index +" - " + Resolved.substring (Resolved_Index) + '\n' +" Drop '" + PARAMETER_REFERENCE_MARKER + "' and '" + REFERENCE_START_MARKER + "' at " + Pattern_Index + '/' + Resolved_Index); is_parameter_reference = true; // Increment Level_Counter. Level_Counter++; /* Drop the opening markers. The first deleteCharAt removes the PARAMETER_REFERENCE_MARKER. The second removes the REFERENCE_MARKER_START, which is bumped into the PARAMETER_REFERENCE_MARKER's position by the latter's deletion. */ Resolved.deleteCharAt (Resolved_Index); Resolved.deleteCharAt (Resolved_Index); resolved_start = Resolved_Index; pattern_start = Pattern_Index; Pattern_Index += 2; } while (Resolved_Index < Resolved.length ()) { switch (Resolved.charAt (Resolved_Index)) { case ESCAPE_MARKER: // Skip the ESCAPE_MARKER. if ((DEBUG & DEBUG_PARSE) != 0) System.out.println (" Skip '" + ESCAPE_MARKER + "' at " + Pattern_Index + '/' + Resolved_Index); Resolved_Index++; Pattern_Index++; if (Resolved_Index < Resolved.length ()) { // Skip the character that was escaped. if ((DEBUG & DEBUG_PARSE) != 0) System.out.println (" Skip escaped '" + Resolved.charAt (Resolved_Index) + "' at " + Pattern_Index + '/' + Resolved_Index); Resolved_Index++; Pattern_Index++; } break; case REFERENCE_START_MARKER: Parse (); break; case PARAMETER_REFERENCE_MARKER: /* May have a parameter reference, but only if the character immediately following the PARAMETER_REFERENCE_MARKER is a REFERENCE_START_MARKER. */ if (Resolved.charAt (Resolved_Index + 1) == REFERENCE_START_MARKER) Parse (); else { // Not a parameter reference. Resolved_Index++; Pattern_Index++; } break; case REFERENCE_END_MARKER: if (resolved_start >= 0) { if ((DEBUG & DEBUG_PARSE) != 0) System.out.println ("*** Reference end at " + Pattern_Index + '/' + Resolved_Index); // Remove the closing marker. if ((DEBUG & DEBUG_PARSE) != 0) System.out.println (" Drop '" + REFERENCE_END_MARKER +"' at " + Pattern_Index + '/' + Resolved_Index); Resolved.deleteCharAt (Resolved_Index); // Decrement the Level_Counter. if (--Level_Counter != 0) { // Resolve the reference. String reference = Resolved.substring (resolved_start, Resolved_Index); if((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (" " + (is_parameter_reference ? "Parameter" : "Field") +" reference: from " + pattern_start + '/' + resolved_start +" to " + Pattern_Index + '/' + Resolved_Index +" - " + reference + '\n' + Resolved.length () + "> " + Resolved); try { Resolved.replace (resolved_start, Resolved_Index, (is_parameter_reference ? Resolve_Parameter_Reference (reference) : Resolve_Field_Reference (reference))); } catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "At parse index " + pattern_start + " in \"" + Pattern + "\".", pattern_start + exception.getErrorOffset ()); } catch (Database_Exception exception) { throw new Database_Exception (exception.getMessage () + '\n' + "At parse index " + pattern_start + " in \"" + Pattern + "\"."); } catch (Unresolved_Reference exception) { throw new Unresolved_Reference (exception.getMessage () + '\n' + "At parse index " + pattern_start + " in \"" + Pattern + "\".", exception.Reference ()); } } // Reset the indices to Parse the resolved value. Pattern_Index = pattern_start; Resolved_Index = resolved_start; if ((DEBUG & DEBUG_PARSE) != 0) System.out.println ("<<< Parse: Next index at: " + Pattern_Index + '/' + Resolved_Index + '\n' + Resolved.length () + "> " + Resolved); return; } else // Throw up: Unexpected reference close. throw Parse_Error ("", -Pattern_Index); default: // Everything else; ignore. Resolved_Index++; Pattern_Index++; } } if (resolved_start >= 0) // Throw up: Unbalanced opening reference. throw Parse_Error ("", (pattern_start + (is_parameter_reference ? 1 : 0))); if ((DEBUG & DEBUG_PARSE) != 0) System.out.println ("<<< Parse: No reference found.\n" + Resolved.length () + "> " + Resolved); return; } /** Resolves a database field reference.

A field reference has the following form:

Catalog.Table.Field:Key

The components are used to {@link PIRL.Database.Database#Select(String, Vector, String) Select} a Database Field value from a Catalog.Table where the table record matches the Key criteria.

N.B.: The field reference is presumed to have been resolved of any nested references.

@param field_reference The field reference String to be resolved by the database. @return The resolved String value. @throws ParseException if the field reference does not have all of the components of the field reference format. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. */ public String Resolve_Field_Reference ( String field_reference ) throws ParseException, Database_Exception, Unresolved_Reference { if((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (">>> Reference_Resolver.Resolve_Field_Reference: \"" + field_reference + '"'); Vector result_vector = new Vector (); if (The_Database != null) { String catalog = Catalog_Name (field_reference), table = Table_Name (field_reference), field = Field_Name (field_reference), key = Key (field_reference); if((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (" catalog: " + catalog + '\n' +" table: " + table + '\n' +" field: " + field + '\n' +" key: " + key); if (catalog == null || table == null || field == null || key == null) throw new ParseException (Error_Message ("Missing " + ((catalog == null) ? "catalog name" : ((table == null) ? "table name" : ((field == null) ? "field name" : "key specification"))) +" in database field reference: \"" + field_reference + "\"."), 0); Vector field_vector = new Vector (); field_vector.add (field); /* The Select method returns a Vector of data record Vectors. The first Vector always contains the names of each field returned, with subsequent Vectors containing the returned data. There may be cases in which we receive more than one result from the query; we should consider only the first. Further, there may be cases in which the query yields no results, in which case the results Vector will hold only one element: the Vector of the field names. We throw an exception when this happens. (We should not throw an exception when more than one matching record is found, however.) The field reference structure guarantees that the Select method will query for only one field, even though we use a form that allows for multiple fields. Thus, we can be pretty sure that we will receive the following from a query: Vector { Vector { }, Vector { First Matching Result }, // If at least one match! Vector { Second Match }, // If more than one match! ... } Thus, as long as the Vector has two or more records, result_vector.get(1).get(0) will be the result of the query that interests us. */ if((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (" The_Database.Table_Reference (" + catalog + ", " + table + ") = " + The_Database.Table_Reference (catalog, table)); result_vector = The_Database.Select (The_Database.Table_Reference (catalog, table), field_vector, key); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (" Query results: " + result_vector); } String value = null; if (result_vector.size () > 1) { value = (String)(((Vector)(result_vector.get (1))).get (0)); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println ("*** Field reference resolved: " + value); } else { if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println ("*** Field reference unresolved. Using " + Default_Value); if ((value = Default_Value) == null) throw new Unresolved_Reference (Error_Message ("Unresolved database field reference \"" + field_reference + "\".\n" +"Using " + Database_Reference ()), field_reference); } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println ("<<< Reference_Resolver.Resolve_Field_Reference: \"" + value + '"'); return value; } /** Resolves a parameter source reference.

A parameter reference has the following form:

Pathname@Sequence[Index]...

The Pathname is used to retrieve a Parameter value from the source Parameters list.

The optional Sequence refers to which of a multiple sequence of Parameters that all match the Pathname is to be selected; the first parameter is Sequence 0.

The Index specifies which element of a Parameter with an Array Value to use; the first element is Index 0. Mulitple Index values access multidimensional Arrays with the left-most specifier accessing an element of the top-most Array, the next specifier accessing an element of the Array Value selected by the previous specifier, etc. If there are less specifiers than the depth of Array Values, then the first non-Array Value of the last Array selected will satisfy the reference (as if [0] had been specified); thus when a parameter reference has no Index specifiers but the referenced Parameter has an Array Value, then the first non-Array Value will be selected regardless of its nesting depth.

N.B.: As a special case, a parameter reference index that is negative results in the Value being returned regardless of whether it is an Array or not. A nested Array value may be selected by providing leading array indices before the negative index in the parameter reference, but any additional array indices provided after a negative index are ignored.

Both the Sequence and Index are subject to mathematical expression evaluation. Any expression that can be evaluated to a numerical value is allowed; the result will be truncated to an integer.

N.B.: The parameter reference is presumed to have been resolved of any nested references.

@param parameter_reference The reference String to be resolved by the source Parameters list. @return The resolved String value. @throws ParseException if the reference contains a syntax error such as an unclosed Array Value element specifier, or a Sequence or Index specifier that does not evaluate to a numerical value. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. @see #Parameter_Reference_Value(String) */ public String Resolve_Parameter_Reference ( String parameter_reference ) throws ParseException, Unresolved_Reference { if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (">>> Reference_Resolver.Resolve_Parameter_Reference: \"" + parameter_reference + '"'); Value parameter_Value = Parameter_Reference_Value (parameter_reference); String value = null; if (parameter_Value != null) { // Get the Value as a String representation. if (parameter_Value.Is_Array ()) { if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (" Parameter Value is an Array -"); value = ""; Value array_Value; Enumeration enumerator = parameter_Value.depthFirstEnumeration (); while (enumerator.hasMoreElements ()) { array_Value = (Value)enumerator.nextElement (); if (! array_Value.Is_Array ()) { if (value.length () != 0) value += ','; try {value += array_Value.String_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (" " + value); } } } else { try {value = parameter_Value.String_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } } if (value == null) { if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println ("*** Parameter reference unresolved. Using " + Default_Value); if ((value = Default_Value) == null) throw new Unresolved_Reference (Error_Message ("Unresolved parameter reference \"" + parameter_reference + "\"."), parameter_reference); } if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println ("<<< Reference_Resolver.Resolve_Parameter_Reference: \"" + value + '"'); return value; } /** Gets the Parameter that resolves a parameter reference.

A parameter reference has the following form:

Pathname@Sequence[Index]...

The Pathname is used to retrieve a Parameter value from the source Parameters list.

The optional Sequence refers to which of a multiple sequence of Parameters that all match the Pathname is to be selected; the first parameter is Sequence 0. The Sequence may be a mathematical expression which is cast to an integer value.

The Array Value Index specifiers are not used here.

N.B.: The parameter_reference String argument may contain nested parameter source references (${parameter_reference}) or database field references ({field_reference}). However, the resolved result is expected to be an unenclosed parameter reference which is used to find the resolving Parameter from this Reference_Resolver's {@link #Parameters() Parameters}.

@param parameter_reference The reference String to be resolved by the source Parameters list. @return The Parameter that resolves the reference, or null if the reference can not be resolved to a Parameter. @throws ParseException if the reference contains a syntax error. @throws Database_Exception if there is a problem accessing the Database. */ public Parameter Resolve_to_Parameter ( String parameter_reference ) throws ParseException, Database_Exception, Unresolved_Reference { Parameter parameter = null; if (parameter_reference != null) { String resolved = Resolve (parameter_reference); try {parameter = Parameter_Reference_Parameter (resolved);} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "While resolving to Parameter: \"" + parameter_reference + "\".", exception.getErrorOffset ()); } } return parameter; } /** Gets the Parameter that resolves a parameter reference.

N.B.: The parameter reference is presumed to have been resolved of any nested references.

@param parameter_reference The reference String to be resolved by the source Parameters list. @return The Parameter that resolves the reference, or null if the reference can not be resolved to a Parameter. @throws ParseException if the reference contains a syntax error. @see #Resolve_to_Parameter(String) */ public Parameter Parameter_Reference_Parameter ( String parameter_reference ) throws ParseException { if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (">>> Reference_Resolver.Parameter_Reference_Parameter: \"" + parameter_reference + '"'); Parameter parameter = null; String pathname = Pathname (parameter_reference); if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (" Pathname - " + pathname); if (pathname != null) { String sequence = Sequence (parameter_reference); if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (" parameter sequence specifier: \"" + Sequence (parameter_reference) + '"'); int count = 0; if (sequence != null) { try {count = (int)Evaluate_to_double (sequence);} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "For Parameter sequence of reference \"" + parameter_reference + "\".", parameter_reference.indexOf (PARAMETER_SEQUENCE_MARKER) + exception.getErrorOffset ()); } } // Find the parameter. parameter = Find_Parameter (pathname, count); } if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) { System.out.print ("<<< Reference_Resolver.Parameter_Reference_Parameter:"); if (parameter == null) System.out.println (" null"); else { System.out.println (""); try {parameter.Write ();} catch (Exception exception) {System.out.println (exception.getMessage ());} } } return parameter; } /** Gets the Value that resolves a parameter reference.

A parameter reference has the following form:

Pathname@Sequence[Index]...

The Pathname is used to retrieve a Parameter Value from the source Parameters list.

The optional Sequence refers to which of a multiple sequence of Parameters, that all match the Pathname, is to be selected; the first is Sequence 0. The Sequence may be a mathematical expression which is cast to an integer value.

The optional Array Value Index specifiers are used to select which Value to use from an Array of Values. When no Index specifiers are present, the implicit "[0]" specifier is used. If the specifiers select an Array Value, then the first non-Array Value of that Array Value is selected.

N.B.: As a special case, a parameter reference index that is negative results in the Value being returned regardless of whether it is an Array or not. A nested Array value may be selected by providing leading array indices before the negative index in the parameter reference, but any additional array indices provided after a negative index are ignored.

N.B.: The parameter_reference String argument may contain nested parameter source references (${parameter_reference}) or database field references ({field_reference}). However, the resolved result is expected to be an unenclosed parameter reference which is used to find the resolving Parameter from this Reference_Resolver's {@link #Parameters() Parameters}.

@param parameter_reference The reference String to be resolved by the source Parameters list. @return The Value that resolves the reference, or null if the reference can not be resolved to a Value. @throws ParseException if the reference contains a syntax error. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. @see #Parameter_Reference_Value(String) */ public Value Resolve_to_Value ( String parameter_reference ) throws ParseException, Database_Exception, Unresolved_Reference { Value value = null; if (parameter_reference != null) { String resolved = Resolve (parameter_reference); try {value = Parameter_Reference_Value (resolved);} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "While resolving to Value: \"" + parameter_reference + "\".", exception.getErrorOffset ()); } } return value; } /** Gets the Value that resolves a parameter reference.

When the parameter reference resolves to an Array Value, the reference is expected to include array indices that will select a non-Array Value from within the Array. However, if no indices are provided the first non-Array Value is selected, regardless of its Array nesting depth. N.B.: As a special case, a parameter reference index that is negative results in the Value being returned regardless of whether it is an Array or not. A nested Array value may be selected by providing leading array indices before the negative index in the parameter reference, but any additional array indices provided after a negative index are ignored. N.B.: The parameter reference (as described for {@link #Resolve_to_Value(String)}) is presumed to have been resolved of any nested references.

@param parameter_reference The parameter reference String. @return A Parameter Value selected by the reference, or null if no Value was selected. @throws ParseException if the reference contains a syntax error. @see #Parameter_Reference_Parameter(String) @see #Array_Indices(String) */ public Value Parameter_Reference_Value ( String parameter_reference ) throws ParseException { if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (">>> Reference_Resolver.Parameter_Reference_Value: \"" + parameter_reference + '"'); Parameter parameter = Parameter_Reference_Parameter (parameter_reference); Value value = null; Get_Value: if (parameter != null) { // Get the value. Vector indices = Array_Indices (parameter_reference); int dimension = 0, index; try {value = parameter.Value ();} catch (PVL_Exception exception) {} while (value != null && value.Is_Array () && dimension < indices.size ()) { if ((index = ((Integer)(indices.get (dimension))).intValue ()) < 0) { // Return the entire Array Value. dimension = indices.size (); // Ignore any remaining dimensions. break Get_Value; } if ((value = value.Get (index)) == null) break; ++dimension; } if (value != null) { if (dimension < indices.size () && (index = ((Integer)(indices.get (dimension))).intValue ()) >= 0) { // Still have indices; ran out of arrays. if (value.Is_Array () || index != 0 || dimension != 0) // Unresolved array reference. value = null; } else { // Use the first non-Array Value. while (value.Is_Array ()) value = value.Get (0); } } } if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) { System.out.print ("<<< Reference_Resolver.Parameter_Reference_Value:"); if (parameter == null) System.out.println (" null"); else { System.out.print ("\n" +" parameter - " + parameter.Path_Name () + '\n' +" value - "); if (value == null) System.out.println ("null"); else { try {value.Write ();} catch (Exception exception) {System.out.println (exception.getMessage ());} } } } return value; } /** Gets the Vector of Array Value dimension indices from a parameter reference.

The parameter reference String may contain zero or more Arrary Value element specifiers enclosed in square brackets ('[' and ']'). The reference String has any nested references {@link #Resolve(String) resolved} to their values. Then the substring within each set of square brackets is evaluated as a mathematical {@link #Resolve_to_double(String) expression}.

Array Value elements are accessed outer-to-inner in left-to-right order: The left-most array element reference applies to the top-most, outer, Value Array; the next array element reference applies to the Value Array selected by the previous reference.

If no Array Value specifier is found an empty indices Vector is returned.

@param parameter_reference The String containing zero or more Array Value element specifiers. @return A Vector of zero or more element indices. @throws ParseException if the references contain a syntax error. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. @see #Resolve_to_double(String) */ public Vector Resolve_Array_Indices ( String parameter_reference ) throws Database_Exception, ParseException, Unresolved_Reference { Vector indices = new Vector (); if (parameter_reference != null) { String resolved = Resolve (parameter_reference); try {indices = Array_Indices (resolved);} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "While resolving Array Indices: \"" + parameter_reference + "\".", exception.getErrorOffset ()); } } return indices; } /** Resolves a reference to a double value.

The reference is fully resolved by an independent Reference_Resolver that shares the Database, source Parameters and Default_Value with this Reference_Resolver. This ensures that the reference will be fully resolved even when the method is not being used by this Reference_Resolver object to resolve its own Pattern; i.e. the Pattern of this Reference_Resolver will not be disturbed.

The resolved reference is parsed by a mathematical expression evaluator to produce a double value.

@param reference The reference String to be resolved. A null @return The resolved double value. If the reference is null, 0.0 will be returned. @throws ParseException if the reference contains a syntax error. @throws Database_Exception if there is a problem accessing the Database. @throws Unresolved_Reference if the reference is unresolved and the Default_Value is null. @see #Resolve(String) @see edu.hws.jcm.data.Parser#Parser() */ public double Resolve_to_double ( String reference ) throws Database_Exception, ParseException, Unresolved_Reference { double value = 0.0; if (reference != null) { String expression = Resolve (reference); try {value = Evaluate_to_double (expression);} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "While resolving to double: \"" + reference + "\".", exception.getErrorOffset ()); } } return value; } /*============================================================================== Utilities */ /** Gets a catlog name from a field reference.

A field reference has catalog, table, field and optional key components:

Catalog.Table.Field:Key

Other than the Key component, if only one component is present, it is the Field; if only two components are present, they are the Catalog and Table.

@param field_reference The field reference to parse. @return The catlog name or null if one is not present. */ public static String Catalog_Name ( String field_reference ) { String catalog_name = null; if (field_reference != null) { int catalog_table_index = field_reference.indexOf (COMPONENTS_DELIMITER), table_field_index = field_reference.indexOf (COMPONENTS_DELIMITER, catalog_table_index + 1), field_key_index = field_reference.indexOf (KEY_MARKER); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print ("Catalog_Name: \"" + field_reference + "\" " + catalog_table_index + ", " + table_field_index + ", " + field_key_index + " "); if (catalog_table_index > 0 && // Must have the first delimiter. (field_key_index < 0 || field_key_index > catalog_table_index)) { catalog_name = field_reference.substring (0, catalog_table_index).trim (); if (catalog_name.length () == 0) catalog_name = null; if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print ("(0, " + catalog_table_index + ") "); } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (catalog_name); } return catalog_name; } /** Gets a table name from a field reference.

A field reference has catalog, table, field and optional key components:

Catalog.Table.Field:Key

Other than the Key component, if only one component is present, it is the Field; if only two components are present, they are the Catalog and Table.

@param field_reference The field reference to parse. @return The table name or null if one is not present. */ public static String Table_Name ( String field_reference ) { String table_name = null; if (field_reference != null) { int catalog_table_index = field_reference.indexOf (COMPONENTS_DELIMITER), table_field_index = field_reference.indexOf (COMPONENTS_DELIMITER, catalog_table_index + 1), field_key_index = field_reference.indexOf (KEY_MARKER); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print (" Table_Name: \"" + field_reference + "\" " + catalog_table_index + ", " + table_field_index + ", " + field_key_index + " "); if (field_key_index >= 0 && catalog_table_index > field_key_index) // In the Key. catalog_table_index = -1; if (field_key_index >= 0 && table_field_index > field_key_index) // In the Key. table_field_index = -1; if (catalog_table_index > 0 && // Must have the first delimiter. (field_key_index < 0 || (field_key_index > catalog_table_index && field_key_index > table_field_index))) { if (table_field_index < 0) { if (field_key_index > 0) table_field_index = field_key_index; else table_field_index = field_reference.length (); } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print ("(" + (catalog_table_index + 1) +", " + table_field_index + ") "); table_name = field_reference.substring (catalog_table_index + 1, table_field_index).trim (); if (table_name.length () == 0) table_name = null; } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (table_name); } return table_name; } /** Gets a field name from a field reference.

A field reference has catalog, table, field and optional key components:

Catalog.Table.Field:Key

Other than the Key component, if only one component is present, it is the Field; if only two components are present, they are the Catalog and Table.

@param field_reference The field reference to parse. @return The field name or null if one is not present. */ public static String Field_Name ( String field_reference ) { String field_name = null; if (field_reference != null) { int catalog_table_index = field_reference.indexOf (COMPONENTS_DELIMITER), table_field_index = field_reference.indexOf (COMPONENTS_DELIMITER, catalog_table_index + 1), field_key_index = field_reference.indexOf (KEY_MARKER); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print (" Field_Name: \"" + field_reference + "\" " + catalog_table_index + ", " + table_field_index + ", " + field_key_index + " "); if (field_key_index >= 0 && catalog_table_index > field_key_index) // In the Key. catalog_table_index = -1; if (field_key_index >= 0 && table_field_index > field_key_index) // In the Key. table_field_index = -1; if (catalog_table_index <= table_field_index) // One or three components. { if (field_key_index < 0) // No Key. field_key_index = field_reference.length (); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print ("(" + (table_field_index + 1) +", " + field_key_index + ") "); field_name = field_reference.substring (table_field_index + 1, field_key_index).trim (); if (field_name.length () == 0) field_name = null; } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (field_name); } return field_name; } /** Gets a key from a field reference.

A field reference has catalog, table, field and optional key components:

Catalog.Table.Field:Key

Other than the Key component, if only one component is present, it is the Field; if only two components are present, they are the Catalog and Table.

@param field_reference The field reference to parse. @return The key or null if one is not present. */ public static String Key ( String field_reference ) { String key = null; if (field_reference != null) { int field_key_index = field_reference.indexOf (KEY_MARKER); if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print (" Key: \"" + field_reference + "\" " + field_key_index + " "); if (field_key_index >= 0) { if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.print ("(" + (field_key_index + 1) + ") "); key = field_reference.substring (field_key_index + 1).trim (); if (key.length () == 0) key = null; } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println (key); } return key; } /** Gets a pathname from a parameter reference.

A parameter reference has a pathname and optional sequence and Array Value element index specifiers:

Pathname@Sequence[Index]...

@param parameter_reference The parameter reference to parse. @return The parameter pathname or null if one is not present. */ public static String Pathname ( String parameter_reference ) { String pathname = null; if (parameter_reference != null) { int index = parameter_reference.indexOf (PARAMETER_SEQUENCE_MARKER); if (index < 0) index = parameter_reference.indexOf (ARRAY_ELEMENT_START_MARKER); if (index > 0) pathname = parameter_reference.substring (0, index).trim (); else pathname = parameter_reference; if (pathname.length () == 0) pathname = null; } return pathname; } /** Gets a parameter sequence from a parameter reference.

A parameter reference has a pathname and optional sequence and Array Value element index specifiers:

Pathname@Sequence[Index]...

@param parameter_reference The parameter reference to parse. @return The parameter sequence or null if one is not present. */ public static String Sequence ( String parameter_reference ) { String sequence = null; if (parameter_reference != null) { int sequence_index = parameter_reference.indexOf (PARAMETER_SEQUENCE_MARKER) + 1; if (sequence_index > 0) { int index = parameter_reference.indexOf (ARRAY_ELEMENT_START_MARKER); if (index < 0) index = parameter_reference.length (); sequence = parameter_reference.substring (sequence_index, index) .trim (); if (sequence.length () == 0) sequence = null; } } return sequence; } /** Gets the parameter Array Value element index specifiers from a parameter reference.

A parameter reference has a pathname and optional sequence and Array Value element index specifiers:

Pathname@Sequence[Index]...

@param parameter_reference The parameter reference to parse. @return The element index specifiers or null if none are present. The element index enclosures are included. */ public static String Index_Specifiers ( String parameter_reference ) { String indices = null; if (parameter_reference != null) { int index = parameter_reference.indexOf (ARRAY_ELEMENT_START_MARKER); if (index >= 0) { indices = parameter_reference.substring (index).trim (); if (indices.length () == 0) indices = null; } } return indices; } /** Gets the Vector of Array Value element indices from a parameter reference.

N.B.: The parameter reference is presumed to have been resolved of any nested references.

@param parameter_reference The parameter reference String containing zero or more Array Value element specifiers. @return A Vector of zero or more element indices. @throws ParseException if the references contain a syntax error. @throws Database_Exception if there is a problem accessing the Database, or a reference is unresolved and the Default_Value is null. @see #Evaluate_to_double(String) */ public static Vector Array_Indices ( String parameter_reference ) throws ParseException { if ((DEBUG & (DEBUG_ARRAY_REFERENCE | DEBUG_PARAMETER_REFERENCE)) != 0) System.out.println (">>> Reference_Resolver.Array_Indices: \"" + parameter_reference + '"'); Vector indices = new Vector (); if (parameter_reference == null) parameter_reference = ""; int dimension = 0, start_index = 0, end_index = 0, length = parameter_reference.length (); for (start_index = parameter_reference.indexOf (ARRAY_ELEMENT_START_MARKER); start_index >= 0; start_index = parameter_reference.indexOf (ARRAY_ELEMENT_START_MARKER, end_index++)) { // Find the end of the array element specifier. int nested = 0; for (end_index = start_index + 1; end_index < length; end_index++) { if (parameter_reference.charAt(end_index) == ESCAPE_MARKER) { // Ignore the next character. ++end_index; continue; } if (parameter_reference.charAt(end_index) == ARRAY_ELEMENT_END_MARKER) { if (nested-- == 0) // Final closing marker. break; // Nested closing marker. continue; } if (parameter_reference.charAt(end_index) == ARRAY_ELEMENT_START_MARKER) // Nested opening marker. ++nested; } if (end_index < length) { // Extract, resolve and evaluate the array element specifier. String specifier = parameter_reference.substring (start_index + 1, end_index); if ((DEBUG & (DEBUG_ARRAY_REFERENCE | DEBUG_PARAMETER_REFERENCE)) != 0) System.out.println (" Reference_Resolver.Array_Indices: \"" + parameter_reference + "\"\n" +" dimension " + dimension + ": \"" + specifier + '"'); try {indices.add (new Integer ((int)Evaluate_to_double (specifier)));} catch (ParseException exception) { throw new ParseException (exception.getMessage () + '\n' + "In Array Value element specifier " + dimension + ": \"" + specifier + "\".", start_index + exception.getErrorOffset ()); } ++dimension; } else throw new ParseException (Error_Message ("Unclosed Array Value element specifier " + dimension + ": \"" + parameter_reference.substring (start_index) + "\"."), start_index); } if ((DEBUG & (DEBUG_ARRAY_REFERENCE | DEBUG_PARAMETER_REFERENCE)) != 0) System.out.println ("<<< Reference_Resolver.Array_Indices: " + indices); return indices; } /** Resolves a reference to a double value.

N.B.: The expression is presumed to have been resolved of any nested references.

@param expression The expression String to be evaluated. @see #Resolve_to_double(String) */ public static double Evaluate_to_double ( String expression ) throws ParseException { if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_ARRAY_REFERENCE)) != 0) System.out.println (">>> Reference_Resolver.Evaluate_to_double: \"" + expression + '"'); double value = 0.0; if (expression != null) { try {value = new Parser ().parse (expression).getVal ();} catch (ParseError exception) { throw new ParseException (Error_Message (exception.getMessage () + '\n' +"Unable to evaluate the expression \"" + expression + "\"\n" +" while parsing the sequence \"" + exception.context.data + "\"\n" +" near sequence position " + exception.context.pos + '.'), 0); } } if ((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println ("<<< Reference_Resolver.Evaluate_to_double: " + value); return value; } /** Tests if a String is an enclosed reference.

The reference may be a parameter reference or a database reference, but it must be completely enclosed in reference markers.

N.B.: No check is made to determine if the reference enclosure, if it exits, is properly balanced.

@param string A String to be tested. @return true if the string is an enclosed reference; false otherwise. */ public static boolean Enclosed_Reference ( String string ) { int level = -1; if (string != null && string.length () > 1 && (string.charAt (0) == Reference_Resolver.REFERENCE_START_MARKER || (string.charAt (0) == Reference_Resolver.PARAMETER_REFERENCE_MARKER && string.charAt (1) == Reference_Resolver.REFERENCE_START_MARKER))) { level = 0; char character; for (int index = 0; index < string.length (); index++) { character = string.charAt (index); if (character == Reference_Resolver.ESCAPE_MARKER) index++; else if (character == Reference_Resolver.REFERENCE_START_MARKER) level++; else if (character == Reference_Resolver.REFERENCE_END_MARKER) level--; } } return level == 0; } /** Gets the index of an {@link #ALTERNATIVE_MARKER} in a String.

The first occurance is found of the {@link #ALTERNATIVE_MARKER} character that is not escaped by a preceeding {@link #ESCAPE_MARKER}, not immediately followed by the same character when this occurs the second character is also skipped), and not contained in a quoted (single or double) sequence.

@param string The String to search. @return The index of the first {@link #ALTERNATIVE_MARKER} in the string, or -1 if the marker is not found. */ public static int Alternative_Marker_Index ( String string ) { int index = -1; if (string != null) { char character; int length = string.length (); while (++index < length) { character = string.charAt (index); if (character == ALTERNATIVE_MARKER) { if ((index + 1) == length || string.charAt (index + 1) != ALTERNATIVE_MARKER) break; ++index; continue; } if (character == '\\') // Skip the escaped character. ++index; else if (character == '"' || character == '\'') { // Skip the quoted sequence. char quote = character; while (++index < length) { character = string.charAt (index); if (character == quote) break; else if (character == '\\') // Skip the escaped character. ++index; } } } if (index >= length) index = -1; } return index; } /*============================================================================== Helpers */ /** Searches for a Parameter from The_Parameters list.

The Match_Mode is used to find an Assignment Parameter with a matching name. When PATTERN_MATCH is being used, the pathname is treated as a regex.

@param pathname The pathname for the parameter to find. @param skip The number of matching parameters to skip over. If skip is negative, then the last matching parameter, if there is one, is selected. @return The matching Parameter, or null if one is not found. */ private Parameter Find_Parameter ( String pathname, int skip ) { if (pathname == null) return null; if((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println (">>> Reference_Resolver.Find_Parameter: " + pathname + ' ' + skip + '\n' + The_Parameters.Description ()); Parameter pattern_parameter = new Parameter (pathname).Classification (Parameter.ASSIGNMENT), last_parameter = null, parameter = null; Selector selection = new Selection (Match_Mode) .Name (true) .And (true) .Classification (true); while ((parameter = The_Parameters.Find (pattern_parameter, selection, last_parameter)) != null) { if (skip == 0) break; if (skip > 0) skip--; last_parameter = parameter; } if (skip < 0) parameter = last_parameter; if((DEBUG & DEBUG_PARAMETER_REFERENCE) != 0) System.out.println ("<<< Reference_Resolver.Find_Parameter: " + ((parameter == null) ? "(not found)" : parameter.Name ())); return parameter; } private static String Array_Elements ( List list ) { String description = ""; Iterator entries = list.iterator (); while (entries.hasNext ()) description += "[" + entries.next () + "]"; return description; } private static String Error_Message ( String message ) { if (message == null || message.length () == 0) message = ID; else message = ID + "\n" + message; return message; } private Database_Exception Database_Error ( String message ) { return new Database_Exception (Error_Message ("Problem accessing " + Database_Reference () + ":\n" + message)); } /** Produces a one line description of the Database.

The description is of the form:

TYPE database on host HOST

The Configuration is used to obtain the {@link Database#TYPE TYPE} and {@link Database#HOST HOST} parameters.

@return A database reference string. */ private String Database_Reference () { if (The_Database == null) return "No Database"; return The_Database.Configuration ().Get_One (Database.TYPE) + " database on host " + The_Database.Configuration ().Get_One (Configuration.HOST); } private ParseException Parse_Error ( String message, int index ) { /* The message will be empty if there was a problem with reference enclosure balance. In this case a negative index for the error indicates an unbalanced closing marker, while a positive index indicates an unbalanced opening marker. Any other message is from the method that is used to resolve the specific type of reference. */ if (message.length () == 0) { if (index < 0) { index = -index; message = "Unexpected closing"; } else message = "Unbalanced opening"; message += " reference marker."; } message += "\nAt parse index " + index + " in \"" + Pattern + "\"."; return new ParseException (Error_Message (message), index); } } pirl-2.3.8/PIRL/Conductor/Colors.java0000644000175000017500000000707111742733131017165 0ustar mathieumathieu/* Conductor Colors PIRL CVS ID: Colors.java,v 1.4 2012/04/16 06:04:09 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.awt.Color; public class Colors { /** Conductor Text style. */ public static Color NOTICE_STYLE = Color.RED, HIGHLIGHT_STYLE = Color.BLUE; /** Sources and Procedures Tables. */ public static Color // Pale yellow. TABLE_HEADER = new Color (247, 255, 165), TABLE_HEADER_SELECTED = Selected_Color (true, TABLE_HEADER), // Ivory. TABLE = new Color (255, 255, 240), TABLE_SELECTED = Selected_Color (true, TABLE), // Leaf green. CURRENT_RECORD = new Color (139,185,126), // Sky blue. PROCESSED_RECORD = new Color (168,189,233), // Fire engine red. FAILURE_STATUS_FIELD = new Color (193, 38, 38), FAILED_SOURCE_FIELD = Color.ORANGE; /** Manager */ public static Color SUCCESS = CURRENT_RECORD, POLLING = PROCESSED_RECORD, FAILURE = FAILURE_STATUS_FIELD, WARNING = new Color (255, 234, 124), POLLING_BLINKER = POLLING, RUNNING_BLINKER = SUCCESS, WARNING_BLINKER = WARNING, FAILURE_BLINKER = FAILURE; /** Maestro Conductor_Table. */ public static Color RUNNING_STATE = SUCCESS, POLLING_STATE = POLLING, RUN_TO_WAIT = new Color (150, 150, 255), WAITING_STATE = new Color (200, 255, 255), HALTED_STATE = FAILURE; /** Maestro Conductor_Matrix. */ public static Color ALTERNATE = Color.WHITE, ALTERNATE_SELECTED = Selected_Color (true, ALTERNATE); /** Kapellmeister. */ public static Color OPENED_THEATER = SUCCESS, CLOSED_THEATER = FAILURE, // Message Monitor text style. MESSAGE_DELIVERED = Color.YELLOW, MESSAGE_SENT = Color.GREEN; /*============================================================================== Utilities */ /** Adjust the color to a possibly "selected" color.

For a selected color ff all of the red, green or blue components of the given color have a value (in the range 0-255) greater than 175 the color is {@link Color#darker() darkened}; otherwise the color is {@link Color#brighter() brightened}.

@param selected true if the color is to be adjusted to the selected color; false if the color is to be returned unchanged. @param color The Color to be adjusted. If selected is false the color will be returned unchanged. @return A Color that will have been modified if selected is true. */ public static Color Selected_Color ( boolean selected, Color color ) { if (selected) { if (color.getRed () > 175 && color.getGreen () > 175 && color.getBlue () > 175) color = color.darker (); else color = color.brighter (); } return color; } } pirl-2.3.8/PIRL/Conductor/Pipeline_Source_Manager.java0000644000175000017500000007464111742733132022453 0ustar mathieumathieu/* Pipeline_Source_Manager PIRL CVS ID: Pipeline_Source_Manager.java,v 1.7 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Iterator; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.decorator.ColorHighlighter; import org.jdesktop.swingx.decorator.Filter; import org.jdesktop.swingx.decorator.FilterPipeline; import org.jdesktop.swingx.decorator.PatternFilter; import org.jdesktop.swingx.decorator.PatternPredicate; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Database.Database; import PIRL.Database.Database_Exception; import PIRL.Viewers.Parameter_View; /** The Pipeline_Source_Manager manages unprocessed source records in a Conductor pipeline Sources table.

Pipeline_Source_Manager provides a GUI to view any unprocessed source records in any Conductor Sources table accessible within the catalog specified by the CATALOG parameter in a PVL-style configuration file specified at the time the application is launched. The operator can optionally delay the processing of selected sources by setting the Conductor_ID to the value specified in the static variable {@link #DELAY_PROCESSING_STRING}, or release a delayed source record by resetting the Conductor_ID to NULL. Source records can also be deleted from Sources tables, but will not remove the source file specified in the Source_Pathname from the filesystem.

Note: The application assumes that all pipeline source tables end with the word "Sources."

The {@link #Usage() usage} description specifies the application command line syntax and describes the GUI elements.

@author Rodney Heyd UA/PIRL @version 1.7 */ public class Pipeline_Source_Manager extends JFrame implements ActionListener { /** Class ID */ public static final String ID = "PIRL.Conductor.Pipeline_Source_Manager (1.7 2012/04/16 06:04:10)"; /** The default configuration file. */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Database.conf"; private Configuration The_Configuration; /** This string is used to update the Conductor_ID of a source to indicate that processing of the source record should be delayed.

The "clear delayed source" functionality will only operate on sources with a Conductor_ID that matches this value. */ public static final String DELAY_PROCESSING_STRING = "Source Delayed"; private Database The_Database; private String Catalog; private Vector Source_Tables; private Vector Sources_List; private Vector Column_Names; private int Source_Pathname_Column; // GUI Elements private JSplitPane Split_Pane; private JComboBox Source_Tables_ComboBox; private JXTable Sources_Display; private Pipeline_Manager_Table_Model Sources_Table_Model; private JTextField Source_Pathname_Filter; private JButton Delay_Selected_Sources, Delete_Selected_Sources, Process_Selected_Sources, Refresh_Sources_List; private JMenuItem Select_All, Select_Delayed, Select_Nulls, Select_None; /** Exit Status Values */ public static final int EXIT_SUCCESS = 0, EXIT_DATABASE_ERROR = 1, EXIT_CONFIGURATION_ERROR = 2, EXIT_COMMAND_LINE_SYNTAX_ERROR = 3; private static String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_MAIN = 1 << 0, DEBUG_INIT = 1 << 2, DEBUG_EVENTS = 1 << 3, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Pipeline_Source_Manager using a Configuration.

A database will be established and the GUI will be displayed.

@param configuration */ public Pipeline_Source_Manager(Configuration configuration) { super("Pipeline_Source_Manager"); The_Configuration = configuration; init(); Create_GUI(); } private Pipeline_Source_Manager() {} /*============================================================================== Helpers */ /** Initializes the class. Initiates the database connection, gets the list of sources tables, etc. */ private void init() { Catalog = The_Configuration.Get_One("CATALOG"); try { The_Database = new Database(The_Configuration); The_Database.Connect(); // Mask out all password parameters. The_Configuration.Case_Sensitive (false); The_Configuration.Set_All ("PASSWORD", "****"); } catch (Database_Exception e) { System.err.println( "A database error occurred while attempting to establish a " + "connection to the database:" + NL + NL + e.getMessage()); System.exit(EXIT_DATABASE_ERROR); } catch (Configuration_Exception e) { System.err.println( "Unable to get the database connection configuration parameters:" + NL + NL + e.getMessage() ); System.exit(EXIT_CONFIGURATION_ERROR); } Source_Tables = Get_Source_Tables(); if(Source_Tables.size() == 0) { String warning_message = "No pipeline source tables were found in the " + Catalog + " catalog. Either no source tables exist, or the account " + "you are using lacks sufficient privileges on the catalog."; PIRL.Viewers.Dialog_Box.Warning(warning_message, this); System.exit(EXIT_DATABASE_ERROR); } Get_Sources(Source_Tables.firstElement()); Sources_Table_Model = new Pipeline_Manager_Table_Model(); Sources_Table_Model.setDataVector(Sources_List, Column_Names); if((DEBUG & DEBUG_INIT) != 0) { for(String source_table: Source_Tables) System.out.println("Found Source Table==>" + source_table); System.out.println("Column_Names==>" + Column_Names); } } private class Pipeline_Manager_Table_Model extends DefaultTableModel { public boolean isCellEditable(int row, int column) { // No cells should be editable. return false; } } /** This method gets the list of pipeline sources tables from the database. @return A vector of strings that are the names of the sources tables contained within the catalog specified within the configuration file. This is list is displayed in the drop box in the GUI. */ private Vector Get_Source_Tables() { Vector source_tables = null; try { source_tables = The_Database.Tables(Catalog); } catch (Database_Exception e) { PIRL.Viewers.Dialog_Box.Error( "A database error occurred while retrieving the list of pipeline " + "sources tables in the " + Catalog + " catalog." + NL + NL + "The list of source tables may be incomplete. Restarting the " + "application is recommended:" + NL + NL + e.getMessage() ); } Iterator table_iterator = source_tables.iterator(); while (table_iterator.hasNext()) { String table_name = table_iterator.next().toString(); if(!table_name.endsWith("Sources")) table_iterator.remove(); } return source_tables; } /** Get the list of pending and delayed sources in the specified table. @param table - A string containing the name of the table from which a list of sources is to be retrieved. */ private void Get_Sources(String table) { String qualified_table = Catalog + "." + table; String conditional = "Conductor_ID is NULL OR Conductor_ID = '" + DELAY_PROCESSING_STRING + "'"; try { Sources_List = The_Database.Select(qualified_table, conditional); } catch (Database_Exception e) { PIRL.Viewers.Dialog_Box.Error( "A database error occurred while retrieving the list of available " + "sources from the database:" + NL + NL + e.getMessage() ); } Column_Names = Sources_List.get(0); Sources_List.remove(Column_Names); return; } private void Delay_Sources() { int [] selected_rows = Sources_Display.getSelectedRows(); int source_number_index = Column_Names.indexOf("Source_Number"); Vector Column_To_Update = new Vector(); Column_To_Update.add("Conductor_ID"); Vector Values = new Vector(); Values.add(DELAY_PROCESSING_STRING); String table = Source_Tables_ComboBox.getSelectedItem().toString(); String warning_message = "The following source numbers were not " + "updated:" + NL; boolean update_successfull = false; boolean failed_updates = false; for(int row: selected_rows) { int model_row_index = Sources_Display.convertRowIndexToModel(row); int source_number = Integer.parseInt( Sources_Table_Model.getValueAt( model_row_index, source_number_index).toString()); try { update_successfull = Delay_Source(source_number, table); } catch (Database_Exception e) { PIRL.Viewers.Dialog_Box.Error( "A database error occurred while attempting to set the " + "Conductor ID to " + DELAY_PROCESSING_STRING + " for source " + "number " + source_number + ":" + NL + NL + e.getMessage() ); } if(!update_successfull) { failed_updates = true; warning_message +=source_number + NL; } } if(failed_updates) { warning_message += NL + "A conductor process may have begun processing" + NL + "these sources." + NL; PIRL.Viewers.Dialog_Box.Warning(warning_message, this); } } private void Process_Sources() { int [] selected_rows = Sources_Display.getSelectedRows(); String warning_message = "The following sources were not reset:" + NL; int source_number_index = Column_Names.indexOf("Source_Number"); boolean source_updated; boolean updates_failed = false; for(int row: selected_rows) { source_updated = false; int model_row_index = Sources_Display.convertRowIndexToModel(row); int source_number = Integer.parseInt( Sources_Table_Model.getValueAt( model_row_index, source_number_index).toString()); try { source_updated = Process_Source( source_number, Source_Tables_ComboBox.getSelectedItem().toString()); } catch (Database_Exception e) { if(DEBUG != 0) e.printStackTrace(); PIRL.Viewers.Dialog_Box.Error( "A database error occurred while attempting to set the " + "Conductor ID to NULL for source number " + source_number + ":" + NL + NL + e.getMessage()); } if(!source_updated) { warning_message += source_number + NL; updates_failed = true; } } warning_message += NL + "This is probably the result of selecting a source " + "that has already been reset." + NL; /* Note: Uncomment the code below to display a dialog when sources are * selected that were not reset. This dialog is rather superfluous as * selecting a source that has not been delayed doesn't actually change * anything. But if you want to know when this happens, uncomment the * code below. */ /* if(updates_failed) { PIRL.Viewers.Dialog_Box.Warning( warning_message ); } */ } private void Delete_Sources() { int [] selected_rows = Sources_Display.getSelectedRows(); String warning_message = "The following source numbers were not " + "deleted:" + NL; int source_number_index = Column_Names.indexOf("Source_Number"); boolean delete_successfull = false; boolean failed_deletions = false; for(int row: selected_rows) { int model_row_index = Sources_Display.convertRowIndexToModel(row); int source_number = Integer.parseInt( Sources_Table_Model.getValueAt( model_row_index, source_number_index).toString()); try { delete_successfull = Delete_Source(source_number, Source_Tables_ComboBox.getSelectedItem().toString()); } catch ( Database_Exception e ) { if(DEBUG != 0) e.printStackTrace(); PIRL.Viewers.Dialog_Box.Error( "A database error occurred while attempting to delete source " + "number " + source_number + ":" + NL + NL + e.getMessage()); } if(!delete_successfull) { warning_message += source_number + NL; failed_deletions = true; } } if(failed_deletions) { warning_message += NL + "A conductor process may have begun processing" + NL + "these sources." + NL; PIRL.Viewers.Dialog_Box.Warning(warning_message, this); } } /*============================================================================= GUI */ private void Create_GUI() { setSize(1024, 768); setJMenuBar(Menu_Bar()); Split_Pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); /*======================================================================== Top panel components */ JPanel top_panel = new JPanel(new BorderLayout()); top_panel.setSize(1024, 400); // The combobox table selector JPanel table_panel = new JPanel(); JLabel source_table_label = new JLabel("Source Table:"); Source_Tables_ComboBox = new JComboBox(Source_Tables); Source_Tables_ComboBox.addActionListener(this); source_table_label.setLabelFor(Source_Tables_ComboBox); Split_Pane.setTopComponent(top_panel); table_panel.add(source_table_label); table_panel.add(Source_Tables_ComboBox); top_panel.add(table_panel, BorderLayout.NORTH); // Source_Pathname filter JPanel filter_panel = new JPanel(); JLabel pathname_filter_label = new JLabel("Source Pathname Filter:"); Source_Pathname_Filter = new JTextField(); Source_Pathname_Filter.setColumns(50); pathname_filter_label.setLabelFor(Source_Pathname_Filter); filter_panel.add(pathname_filter_label); filter_panel.add(Source_Pathname_Filter); top_panel.add(filter_panel, BorderLayout.CENTER); Source_Pathname_Column = Sources_Table_Model.findColumn("Source_Pathname"); Source_Pathname_Filter.getDocument().addDocumentListener( new DocumentListener() { public void changedUpdate(DocumentEvent arg0) { } public void insertUpdate(DocumentEvent arg0) { String filter_text = Source_Pathname_Filter.getText (); Sources_Display.setFilters ( new FilterPipeline( new Filter[] { new PatternFilter( filter_text + ".*", 0, Source_Pathname_Column)}) ); } public void removeUpdate(DocumentEvent arg0) { String filter_text = Source_Pathname_Filter.getText (); Sources_Display.setFilters ( new FilterPipeline( new Filter[] { new PatternFilter( filter_text + ".*", 0, Source_Pathname_Column)}) ); } }); // Delay Selected Sources Delay_Selected_Sources = new JButton("Delay Selected Sources"); Delay_Selected_Sources.addActionListener(this); Delay_Selected_Sources.setEnabled(false); // Process Selected Sources Process_Selected_Sources = new JButton("Clear Delay Flag On Selected Sources"); Process_Selected_Sources.addActionListener(this); Process_Selected_Sources.setEnabled(false); // Delete Selected Sources Delete_Selected_Sources = new JButton("Delete Selected Sources"); Delete_Selected_Sources.addActionListener(this); Delete_Selected_Sources.setEnabled(false); // Refresh Sources List Refresh_Sources_List = new JButton("Refresh Sources List"); Refresh_Sources_List.addActionListener( this ); Refresh_Sources_List.setEnabled( true ); JPanel button_panel = new JPanel(); button_panel.add(Delay_Selected_Sources); button_panel.add(Process_Selected_Sources); button_panel.add(Delete_Selected_Sources); button_panel.add( Refresh_Sources_List ); top_panel.add(button_panel, BorderLayout.SOUTH); /*======================================================================== Bottom panel components (the table) */ Sources_Display = new JXTable(Sources_Table_Model); Sources_Display.setColumnControlVisible(true); Sources_Display.packAll(); Sources_Display.setHorizontalScrollEnabled(true); Sources_Display.getSelectionModel().addListSelectionListener( new ListSelectionListener() { public void valueChanged(ListSelectionEvent arg0) { int[] selected_rows = Sources_Display.getSelectedRows(); if(selected_rows.length > 0) { Delay_Selected_Sources.setEnabled( true ); Process_Selected_Sources.setEnabled( true ); Delete_Selected_Sources.setEnabled( true ); } else { Delay_Selected_Sources.setEnabled( false ); Process_Selected_Sources.setEnabled( false ); Delete_Selected_Sources.setEnabled( false ); } } }); set_table_highlighters(); JScrollPane scrollpane = new JScrollPane(Sources_Display); scrollpane.setSize(1024, 500); Split_Pane.setBottomComponent(scrollpane); add(Split_Pane); } private JMenuBar Menu_Bar() { JMenuBar MenuBar = new JMenuBar(); JMenu file_menu = new JMenu("File"); MenuBar.add(file_menu); JMenuItem view_configuration = new JMenuItem("View Configuration"); // Configuration Viewer. Usefull for viewing or debuging the // configuration. view_configuration.addActionListener ( new ActionListener() { public void actionPerformed ( ActionEvent e ) { Parameter_View config_view = new Parameter_View( The_Configuration); config_view.setVisible ( true ); } }); file_menu.add(view_configuration); JMenuItem exit_app = new JMenuItem("Exit", KeyEvent.VK_X); exit_app.setEnabled( true ); exit_app.setActionCommand( "Exit" ); exit_app.addActionListener ( new ActionListener(){ public void actionPerformed ( ActionEvent e ) { System.exit ( EXIT_SUCCESS ); } }); file_menu.add(exit_app); JMenu select_menu = new JMenu("Select"); Select_All = new JMenuItem("Select All"); Select_All.addActionListener(this); select_menu.add(Select_All); Select_Delayed = new JMenuItem("Select Delayed"); Select_Delayed.addActionListener(this); select_menu.add(Select_Delayed); Select_Nulls = new JMenuItem("Select Nulls"); select_menu.add(Select_Nulls); Select_Nulls.addActionListener(this); Select_None = new JMenuItem("Deselect All"); select_menu.add(Select_None); Select_None.addActionListener(this); MenuBar.add(select_menu); return MenuBar; } private void set_table_highlighters() { int conductor_id_column = Sources_Table_Model.findColumn ( "Conductor_ID" ); Pattern delayed_pattern = Pattern.compile( DELAY_PROCESSING_STRING ); PatternPredicate color_predicate = new PatternPredicate( delayed_pattern, conductor_id_column); ColorHighlighter delayed_highlighter = new ColorHighlighter(); delayed_highlighter.setHighlightPredicate( color_predicate ); delayed_highlighter.setForeground( Color.BLUE); delayed_highlighter.setBackground( Color.WHITE ); delayed_highlighter.setSelectedBackground( Color.BLUE ); delayed_highlighter.setSelectedForeground( Color.WHITE ); Sources_Display.addHighlighter( delayed_highlighter ); } /*============================================================================== Source methods */ /** This method deletes a source number from a specified table. @param source_number - the source_number of the source to be deleted. @param table - the table the source is to be deleted from @return true if the sources was successfully deleted, false otherwise. A false return value means that either the source id doesn't exist, or a conductor has started processing the source. @throws Database_Exception if a database error occurs while attempting to delete the source. */ public boolean Delete_Source(int source_number, String table) throws Database_Exception { boolean source_deleted = true; String warning_message = "The following source numbers were not " + "deleted:" + NL; String qualified_table = Catalog + "." + table; String condition_clause; int deleted_row_count = 0; condition_clause = "Source_Number=" + source_number + " AND " + "(Conductor_ID is NULL OR Conductor_ID = '" + DELAY_PROCESSING_STRING + "')"; deleted_row_count = The_Database.Delete(qualified_table, condition_clause); if(deleted_row_count == 0) { source_deleted = false; warning_message +=source_number + NL; } return source_deleted; } /** This method makes a source available for processing that was delayed using the {@link #Delay_Source(int, String)} method. It will only reset sources for which the Conductor ID field has been set to the value of {@value #DELAY_PROCESSING_STRING} @param source_number - the source_number to operate on @param table - the table containing the sources to be reset @return true if the database update was successfull. @throws Database_Exception if a database error occurred during the update. @see #Delay_Source(int, String) @see #DELAY_PROCESSING_STRING */ public boolean Process_Source(int source_number, String table) throws Database_Exception { Vector Column_To_Update = new Vector(); Column_To_Update.add("Conductor_ID"); Vector Values = new Vector(); Values.add("NULL"); String qualified_table = Catalog + "." + table; String condition_clause; condition_clause = "Source_Number=" + source_number + " AND Conductor_ID='" + DELAY_PROCESSING_STRING + "'"; int records_updated = The_Database.Update(qualified_table, Column_To_Update, Values, condition_clause); if( records_updated == 1 ) return true; else return false; } /*============================================================================== GUI Event Handlers */ public void actionPerformed(ActionEvent event) { Object source = event.getSource(); if((DEBUG & DEBUG_EVENTS) != 0) { System.out.println("Caught event from " + source.getClass().getName()); } if(source.equals(Source_Tables_ComboBox)) { Get_Sources(Source_Tables_ComboBox.getSelectedItem().toString()); Sources_Table_Model.setDataVector(Sources_List, Column_Names); Sources_Table_Model.fireTableDataChanged(); Sources_Display.packAll(); } if(source.equals(Delay_Selected_Sources)) { Delay_Sources(); Get_Sources(Source_Tables_ComboBox.getSelectedItem().toString()); Sources_Table_Model.setDataVector(Sources_List, Column_Names); Sources_Table_Model.fireTableDataChanged(); Sources_Display.packAll(); } if(source.equals(Process_Selected_Sources)) { Process_Sources(); Get_Sources(Source_Tables_ComboBox.getSelectedItem().toString()); Sources_Table_Model.setDataVector(Sources_List, Column_Names); Sources_Table_Model.fireTableDataChanged(); Sources_Display.packAll(); } if(source.equals(Delete_Selected_Sources)) { Delete_Sources(); Get_Sources(Source_Tables_ComboBox.getSelectedItem().toString()); Sources_Table_Model.setDataVector(Sources_List, Column_Names); Sources_Table_Model.fireTableDataChanged(); Sources_Display.packAll(); } if(source.equals( Refresh_Sources_List )) { Get_Sources(Source_Tables_ComboBox.getSelectedItem().toString()); Sources_Table_Model.setDataVector( Sources_List, Column_Names ); Sources_Table_Model.fireTableDataChanged(); Sources_Display.packAll(); } if(source.equals(Select_All)) { Sources_Display.selectAll(); } if(source.equals(Select_Delayed)) { Sources_Display.clearSelection(); int conductor_index = Column_Names.indexOf("Conductor_ID"); int row_count = Sources_Table_Model.getRowCount(); for(int row = 0; row < row_count; row++) { Object cell_value = Sources_Table_Model.getValueAt(row, conductor_index); if(cell_value != null && cell_value .equals(DELAY_PROCESSING_STRING)) { Sources_Display.addRowSelectionInterval(row, row); } } } if(source.equals(Select_Nulls)) { Sources_Display.clearSelection(); int conductor_index = Column_Names.indexOf("Conductor_ID"); int row_count = Sources_Table_Model.getRowCount(); for(int row = 0; row < row_count; row++) { if(Sources_Table_Model.getValueAt(row, conductor_index) == null) { Sources_Display.addRowSelectionInterval(row, row); } } } if(source.equals(Select_None)) Sources_Display.clearSelection(); } /** This method will set the Conductor ID field of a source record to the {@link #DELAY_PROCESSING_STRING} to cause a conductor to skip the specified source. This gives the operator a very limited ability to prioritize sources by delaying the processing of some source fields while allowing others to be processed. @param source_number - the source number to be delayed @param table - the pipeline source table containing the source to be delayed @return true if the Conductor ID of the given source was successfully updated. A false return value indicates that either the source does not exist, or a conductor has already begun processing the source. @throws Database_Exception if a database error occurs while attempting to update the source record. @see #Process_Source(int, String) @see #DELAY_PROCESSING_STRING */ public boolean Delay_Source(int source_number, String table) throws Database_Exception { Vector Column_To_Update = new Vector(); Column_To_Update.add("Conductor_ID"); Vector Values = new Vector(); Values.add(DELAY_PROCESSING_STRING); String qualified_table = Catalog + "." + table; String condition_clause; int updated_row_count; condition_clause = "Source_Number=" + source_number + " AND Conductor_ID is NULL"; updated_row_count = The_Database.Update( qualified_table, Column_To_Update, Values, condition_clause); if(updated_row_count == 0) return false; else return true; } /*============================================================================== Application Methods */ /** Manage sources in a Conductor Pipeline Source Table.

The command line syntax is described in the {@link #Usage()} method.

Exit Status Values:

{@link #EXIT_SUCCESS}
No errors occurred.
{@link #EXIT_DATABASE_ERROR}
There was a problem accessing the database.
{@link #EXIT_CONFIGURATION_ERROR}
There was a problem with the Configuration file.
{@link #EXIT_COMMAND_LINE_SYNTAX_ERROR}
The command line contains invalid syntax.

@param arguments The {@link #Usage() command line arguments}. */ public static void main(String[] arguments) { String configuration_filename = null; for (int count = 0; count < arguments.length; count++) { if (arguments[count].length () == 0) continue; if (arguments[count].charAt (0) == '-') { switch (arguments[count].charAt (1)) { case 'C': // -Database Configuration case 'c': String name = null; if (++count == arguments.length || arguments[count].charAt (0) == '-') { // Use the default configuration file. name = DEFAULT_CONFIGURATION_FILENAME; --count; } else name = arguments[count]; if (configuration_filename == null) configuration_filename = name; else { Usage (); } break; case 'H': // -Help case 'h': Usage (); System.exit ( EXIT_SUCCESS ); default: System.out.println ("Unrecognized switch: " + arguments[count]); Usage (); System.exit(EXIT_COMMAND_LINE_SYNTAX_ERROR); } } } if(configuration_filename == null) configuration_filename=DEFAULT_CONFIGURATION_FILENAME; Configuration configuration=null; try { configuration = new Configuration(configuration_filename); } catch (Configuration_Exception e) { System.err.println( "A configuration error occured while reading the configuration " + "file: " + configuration_filename + NL + NL + e.getMessage() ); System.exit(EXIT_CONFIGURATION_ERROR); } final Configuration Configuration = configuration; SwingUtilities.invokeLater( new Runnable() { public void run() { Pipeline_Source_Manager manager = new Pipeline_Source_Manager(Configuration); manager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); manager.setVisible(true); } }); } /** Prints the command line usage syntax.

Usage: Pipeline_Source_Manager <Options>
  Options -
    [-Configuration <source>
      Default: Database.conf
    [-Help]

Configuration

Pipeline_Source_Manager requires a Configuration file which contains database and catalog connection information. The provider Conductor.conf file is a suitable template to use for this purpose.

The GUI consists of:

A Source Table selector
Used to select the effective Sources table.
A Source_Pathname filter
A full or partial pathname is used to filter the source records list to contain only the records with matching Source_Pathname field values.
Operations buttons
Used to perform the coresponding delay/undelay or delete operations on the source records selected in the Sources table.

Note: If a Conductor has started processing a source record after the time that a Sources table was read from the database, the Pipeline_Source_Manager will not operate on that source record. This prevents the Conductor_ID from being set to a delayed status if a Conductor has already acquired the source record.

Sources table
A table listing the unprocessed and delayed sources found in the currently selected Sources Table. One or more of the source records listed in the table may be selected and then an Operation button used to perform the corresponding operation on the selected source records.
*/ public static void Usage() { System.out.println ( ID + NL + "Usage: Pipeline_Source_Manager " + NL + " Options -" + NL + " [-Configuration ]" + NL + " Default: " + DEFAULT_CONFIGURATION_FILENAME + NL + " [-Help]" ); } } pirl-2.3.8/PIRL/Conductor/Procedures_Table.java0000644000175000017500000001076511742733132021153 0ustar mathieumathieu/* Procedures_Table PIRL CVS ID: Procedures_Table.java,v 1.6 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import org.jdesktop.swingx.JXTable; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.table.TableCellRenderer; import javax.swing.JLabel; import javax.swing.BorderFactory; import java.awt.Component; import java.awt.Color; import java.awt.Font; import java.util.Vector; /** A Procedures_Table is a table view of a Procedures_Table_Model.

@author Bradford Castalia, UA/HiROC @version 1.6 */ public class Procedures_Table extends JXTable { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Procedures_Table (1.6 2012/04/16 06:04:10)"; private Font Emphasis_Font; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_RENDERER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Procedures_Table ( Procedures_Table_Model table_model ) { super (table_model); setColumnSelectionAllowed (true); setRowSelectionAllowed (false); getColumnModel ().getSelectionModel () .setSelectionMode (ListSelectionModel.SINGLE_SELECTION); addMouseListener (new Table_Mouse_Listener ()); setBackground (Colors.TABLE); getTableHeader ().setBackground (Colors.TABLE_HEADER); Emphasis_Font = getFont ().deriveFont (Font.BOLD); /* WARNING: These are JXTable specific methods. */ setColumnControlVisible (true); setSortable (false); setDefaultRenderer (String.class, new Procedures_Table_Cell_Renderer ()); } /*============================================================================== Accessors */ /* If not provided by the base class. public int convertRowIndexToModel ( int view_row ) {return view_row;} */ /*============================================================================== Procedures_Table_Cell_Renderer */ private class Procedures_Table_Cell_Renderer extends JLabel implements TableCellRenderer { public Procedures_Table_Cell_Renderer () { setOpaque (true); setBorder (BorderFactory.createEmptyBorder (0, 5, 0, 0)); } public Component getTableCellRendererComponent ( JTable table, Object value, boolean selected, boolean focused, int row, int column ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (">>> Procedures_Table_Cell_Renderer: " + row + '/' + ((JXTable)table).convertRowIndexToModel (row) + ',' + column + '/' + table.convertColumnIndexToModel (column) + ' ' + value); row = ((JXTable)table).convertRowIndexToModel (row); Procedures_Table_Model model = (Procedures_Table_Model)table.getModel (); Vector record = (Vector)model.Record (row); Color color = table.getBackground (); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println (" Current_Row: " + model.Current_Row); if (row <= model.Current_Row) { // Processed record. setFont (Emphasis_Font); if (row == model.Current_Row) color = Colors.CURRENT_RECORD; else color = Colors.PROCESSED_RECORD; } else setFont (table.getFont ()); if (selected) { if (color.getRed () > 200 && color.getGreen () > 200 && color.getBlue () > 200) color = color.darker (); else color = color.brighter (); } setBackground (color); setValue (value); if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("<<< Procedures_Table_Cell_Renderer"); return this; } public void setValue ( Object value ) {setText ((value == null) ? "" : value.toString ());} } // Procedures_Table_Cell_Renderer } pirl-2.3.8/PIRL/Conductor/Stream_Logger.java0000644000175000017500000003617511742733132020466 0ustar mathieumathieu/* Stream_Logger PIRL CVS ID: Stream_Logger.java,v 1.17 2012/04/16 06:04:10 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import java.io.Writer; import java.util.Vector; import java.util.ListIterator; /** A Stream_Logger is a Thread that is used to forward the lines read from an InputStream to one or more Writers.

Once started a Stream_Logger reads a line of input from its InputStream and then writes the line to each Writer in its list. Writers may be added or removed from the list at any time, before or after the start of the Stream_Logger. If no Writer has been bound to the Stream_Logger the input stream is still read and buffered.

Lines read are stored in a buffer up to an amount that may be changed at any time.

A Stream_Logger stops when an end of file or IOException has been encountered on its InputStream. However, it may be notified to end before reading the next line. It may also be notified to close if no input has arrived, but continue to the normal end of file (or IOException) if data has already arrived.

@author Bradford Castalia - UA/PIRL @version 1.17 @see Thread */ public class Stream_Logger extends Thread { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Stream_Logger (1.17 2012/04/16 06:04:10)"; /** The default {@link #Polling_Interval(long) polling interval}. */ public static final int DEFAULT_POLLING_INTERVAL = 300; // Initial read polling interval (msec). private volatile long Polling_Interval = DEFAULT_POLLING_INTERVAL; // Input stream. private String Stream_Name; private static int READER_BUFFER_SIZE = 1024; private BufferedReader Data_Reader; // Output log writers. private Vector Writers = new Vector (); /** The minimum {@link #Buffer_Size(int) buffer size}. */ public static final int MINIMUM_BUFFER_SIZE = READER_BUFFER_SIZE; /** The default {@link #Buffer_Size(int) buffer size}. */ public static final int DEFAULT_BUFFER_SIZE = 8 * READER_BUFFER_SIZE; private int Line_Buffer_Size = DEFAULT_BUFFER_SIZE; // Last lines buffer. private StringBuffer Line_Buffer = new StringBuffer (Line_Buffer_Size); private static String NL; static { if ((NL = System.getProperty ("line.separator")) == null) NL = "\n"; } private static int NL_LENGTH = NL.length (); // Stream_Logger state. private static final int IDLE = 0, ABORT = 1 << 0, CLOSE = 1 << 1, READING = 1 << 2; private volatile int State = IDLE; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_LOGGING = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Stream_Logger connecting an InputStream with a log Writer.

The InputStream is wrapped in an InputStreamReader and a BufferedReader. The Writer, if not null, is the first entry in the Vector of log Writers.

Note: This Thread is marked as a {@link Thread#setDaemon daemon} so it does not need to finish before the user application can exit.

@param stream_name The name of the stream to prefix to each line logged along with a ": " separator. If null no line prefix is added. @param input_stream The InputStream to be read. @param writer A Writer to use for logging lines. If null, the Stream_Logger will not have any initial Writer. @throws IllegalArgumentException If the input_stream is null. */ public Stream_Logger ( String stream_name, InputStream input_stream, Writer writer ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Stream_Logger: " + stream_name + ((writer == null) ? "" : " with Writer")); if (stream_name == null) stream_name = ""; else stream_name += ": "; Stream_Name = stream_name; if (writer != null) Writers.add (writer); if (input_stream == null) throw new IllegalArgumentException (ID + NL + "Can't construct a Stream_Logger on a null InputStream."); Data_Reader = new BufferedReader (new InputStreamReader (input_stream), READER_BUFFER_SIZE); setDaemon (true); } /** Constructs a Stream_Logger for an InputStream without an initial log Writer.

@param stream_name The name of the stream to prepend to each line logged. This may be null. @param input_stream The InputStream to be read. @throws IllegalArgumentException If the input_stream is null. @see #Stream_Logger(String, InputStream, Writer) */ public Stream_Logger ( String stream_name, InputStream input_stream ) {this (stream_name, input_stream, null);} private Stream_Logger () {} /*============================================================================== Accessors */ /** Adds a Writer to the list of writers where read lines will sent.

If the Writer is already in the list it is not added.

@param writer The Writer to add. @return true if the Writer was added to the list; false if it was already in the list or is null. @see #Remove(Writer) */ public boolean Add ( Writer writer ) { if (writer == null) return false; boolean added; synchronized (Writers) { added = ! Writers.contains (writer); if (added) Writers.add (writer); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Stream_Logger.Add: " + Stream_Name + ' ' + added); return added; } /** Removes a Writer from the Vector of log Writers.

@param writer The Writer to remove. @return true if the Writer was removed to the list; false if it was not in the list or is null. @see #Add(Writer) */ public boolean Remove ( Writer writer ) { if (writer == null) return false; synchronized (Writers) {return Writers.remove (writer);} } /** Gets the last-lines-read buffer.

If the Stream_Logger is {@link #run() running} the contents of the buffer will change if a new line arrives. Obviously, the buffer returned should not be modified while the Stream_Logger is running. N.B.: Synchronize on the buffer if necessary, though this will block reading of further lines until the lock is released.

@return A StringBuffer containing the last lines read from the InputStream. */ public StringBuffer Buffer () {return Line_Buffer;} /** Sets the maximum size of the last-lines-read buffer.

Note: The buffer size is never allowed to be less than the {@link #MINIMUM_BUFFER_SIZE}.

Warning: Do not call this method while sychronized on the buffer as this will result in a deadlock.

@param size The maximum number of characters allowed in the buffer. If the buffer is currently larger than this amount whole lines will be removed from the front of the buffer to bring the content amount below the size. @return This Stream_Logger. */ public Stream_Logger Buffer_Size ( int size ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Stream_Logger.Buffer_Size: " + Stream_Name + ' ' + size); if (size < MINIMUM_BUFFER_SIZE) size = MINIMUM_BUFFER_SIZE; synchronized (Line_Buffer) { if (size < Line_Buffer.length ()) { // Buffer overflow; drop leading line(s). int index = Line_Buffer.indexOf (NL, size); if (index < 0) // The current content won't fit; drop it all. index = Line_Buffer.length (); else index += NL_LENGTH; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Length " + Line_Buffer.length () + "; drop first " + index + " characters."); Line_Buffer.delete (0, index); } } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Stream_Logger.Buffer_Size: " + size); Line_Buffer_Size = size; return this; } /** Gets the maximum size of the last-lines-read buffer.

@return The maximum number of characters allowed in the buffer. @see #Buffer_Size(int) */ public int Buffer_Size () {return Line_Buffer_Size;} /** Clears the last-lines-read buffer.

Warning: Do not call this method while sychronized on the buffer as this will result in a deadlock. */ public void Clear_Buffer () { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Stream_Logger.Clear_Buffer: " + Stream_Name); synchronized (Line_Buffer) {Line_Buffer.delete (0, Line_Buffer.length ());} } /** Sets the polling interval to check for the arrival of data.

The polling interval is only used while waiting for data to arrive on the input stream. After that point blocking reads are used.

A short polling interval has the disadvantage of consuming more CPU cycles while waiting for data to arrive. This can have an adverse affect on system performance, especially if no data arrives for a long time.

A long polling interval has the disadvantage of introducing a delay up to the interval time between when data first arrives and input actually begins. This is generally not a problem if the delay is not so long as to interfere with data delivery downstream or cause data loss due to buffer overflow upstream.

@param interval The amount of time to wait, in milliseconds, between tests to see if data has arrived. The minimum time is one millisecond. This initial default is {@link #DEFAULT_POLLING_INTERVAL} milliseconds. @return This Stream_Logger. @see #Close() */ public Stream_Logger Polling_Interval ( long interval ) { if (interval < 1) interval = 1; Polling_Interval = interval; return this; } /** Gets the polling interval to check for the initial arrival of data.

@return The polling interval in milliseconds. @see #Polling_Interval(long) */ public long Polling_Interval () {return Polling_Interval;} /*============================================================================== Runnable */ /** Runs the Stream_Logger.

Each line of input {@link BufferedReader#readLine() read} from the InputStream is written to each Writer in the Vector of Writers. Each line written is prepended with the stream name followed by ": ", unless no stream name was provided. The system's line ending sequence, from the "line.separator" property, is appended to each line written.

Each line read is also appended to the last-lines-read buffer. Only entire lines are buffered; lines that are too big to fit in the buffer are dropped. Lines, and only entire lines, are removed from the front of the buffer as needed to make room for new lines to be added at the end.

Logging continues until an end-of-file or IOException is encountered on the InputStream, or the logger is told to {@link #End() End}.

N.B.: If a Writer throws an IOException it is removed from the list of Writers. */ public void run () {log_lines ();} /** Ends the Stream_Logger.

Logging ends before the next line is read, but after the current line has finished logging.

N.B.: Logging stops even if there may be more input available from the InputStream after the current line. */ public void End () {State |= ABORT;} /** Effectively closes the input stream.

The input stream is initially polled for a ready condition (characters are available) before begining to read lines. This prevents the Stream_Logger from becoming blocked on input which will never arrive, thus allowing it to be externally halted. However, it may not be known if input has arrived or not.

If input has not arrived then the Stream_Logger is to stop polling and {@link #End() End}. If input has arrived then the Stream_Logger is to continue reading until EOF or an IOException occurs. */ public void Close () {State |= CLOSE;} /*------------------------------------------------------------------------------ Logging */ private void log_lines () { State |= READING; if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (">>> Stream_Logger.log_lines: " + Stream_Name + " State = " + State + " - " + Logging_State ()); String line = null; try { // Wait for data to arrive. while (State == READING && ! Data_Reader.ready ()) { try {sleep (Polling_Interval);} catch (InterruptedException e) {} } if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (" Stream_Logger.log_lines: " + Stream_Name + " State = " + State + " - " + Logging_State ()); if (Data_Reader.ready ()) { while ((State & ABORT) == 0) { // Read until EOF or IOException. line = Data_Reader.readLine (); if (line == null) { // EOF. if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (" Stream_Logger.log_lines: " + Stream_Name + " EOF"); break; } log (line); yield (); if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (" Stream_Logger.log_lines: " + Stream_Name + " State = " + State + " - " + Logging_State ()); } } } catch (IOException exception) { if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (" Stream_Logger.log_lines: " + Stream_Name + " IOException - " + exception.getMessage ()); } if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println ("<<< Stream_Logger.log_lines: " + Stream_Name + " State = " + State + " - " + Logging_State ()); State = IDLE; } private String Logging_State () { String state = ""; if (State == IDLE) state = "IDLE"; else { if ((State & READING) != 0) state = "READING"; if ((State & CLOSE) != 0) { if (state.length () != 0) state += " "; state += "CLOSE"; } if ((State & ABORT) != 0) { if (state.length () != 0) state += " "; state += "ABORT"; } } return state; } private void log ( String line ) { if ((DEBUG & DEBUG_LOGGING) != 0) System.out.println (">-< Stream_Logger.log: " + Stream_Name + " " + line); // Send the line to each writer. Writer writer; synchronized (Writers) { ListIterator writers = Writers.listIterator (); while (writers.hasNext ()) { writer = writers.next (); try { synchronized (writer) { writer.write (Stream_Name + line + NL); writer.flush (); } } catch (IOException exception) {writers.remove ();} } } // Put the line in the buffer. int index = Line_Buffer.length () + line.length (); if (index > Line_Buffer_Size) { // Buffer overflow; drop leading line(s). index = Line_Buffer.indexOf (NL, index -= Line_Buffer_Size); if (index < 0) // The line won't fit; drop it. return; index += NL_LENGTH; Line_Buffer.delete (0, index); } Line_Buffer.append (line); } } // End of Stream_Logger class. pirl-2.3.8/PIRL/Conductor/tests/0000755000175000017500000000000012052546525016222 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/tests/count_up_to.c0000644000175000017500000000047110335333574020726 0ustar mathieumathieu/* count_up_to count_up_to.c,v 1.2 2005/11/12 09:35:56 castalia Exp */ #include main (int argc, char** argv) { int count = 0, limit = 0; if (argc > 1) limit = atoi (argv[1]); while (count++ < limit) { sleep (1); fprintf (stdout, "%d of %d seconds.\n", count, limit); } exit (limit); } pirl-2.3.8/PIRL/Conductor/tests/exit_status.c0000644000175000017500000000050410775265300020740 0ustar mathieumathieu/* exit exit_status.c,v 1.3 2008/04/03 23:39:44 castalia Exp */ #include #include main (int argc, char** argv) { fprintf (stdout, "%s 1.3 2008/04/03 23:39:44\n", argv[0]); int status = 0; if (argc > 1) status = atoi (argv[1]); fprintf (stderr, "exit status %d\n", status); exit (status); } pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Sources-Create.MySQL0000644000175000017500000000117310226217522024630 0ustar mathieumathieu# Proc_Test.Test_Sources-Create.SQL # # PIRL CVS ID: Proc_Test.Test_Sources-Create.MySQL,v 1.3 2005/04/10 12:42:58 castalia Exp # Create the catalog if it doesn't exist. CREATE DATABASE IF NOT EXISTS Proc_Test; # Create the table if it doesn't exist. CREATE TABLE IF NOT EXISTS Proc_Test.Test_Sources ( Source_Number INT UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT PRIMARY KEY, Source_Pathname TEXT NOT NULL, Source_ID VARCHAR(64) BINARY, Conductor_ID VARCHAR(64), Status TEXT, Log_Pathname TEXT, Last_Update TIMESTAMP ); # Insert a sample record. INSERT INTO Proc_Test.Test_Sources SET Source_Pathname="test_source"; pirl-2.3.8/PIRL/Conductor/tests/test_Native_Methods.java0000644000175000017500000000244111742733134023035 0ustar mathieumathieu/* test_Native_Methods PIRL CVS ID: test_Native_Methods.java,v 1.4 2012/04/16 06:04:12 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Conductor.Native_Methods; public class test_Native_Methods { public static void main ( String[] args ) { System.out.println (Native_Methods.ID); System.out.println ("Native_Methods.PID () = " + Native_Methods.PID ()); System.exit (0); } } pirl-2.3.8/PIRL/Conductor/tests/Makefile0000644000175000017500000000055011231221761017650 0ustar mathieumathieu# Makefile for Java classes # PIRL CVS ID: Makefile,v 1.7 2009/07/21 02:12:01 castalia Exp JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< TESTS = test_Reference_Resolver.class \ test_Native_Methods.class \ test_String_Vector_Comparator.class all: ${TESTS} clean: rm -f *.class pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Procedures-Create.PSQL0000644000175000017500000000364511106723521025137 0ustar mathieumathieu-- Proc_Test.Test_Procedures-Create.SQL -- -- PIRL CVS ID: Proc_Test.Test_Procedures-Create.PSQL,v 1.2 2008/11/13 04:04:33 castalia Exp -- Create the schema. CREATE SCHEMA Proc_Test; -- Drop the table if it exists. DROP TABLE IF EXISTS Proc_Test.Test_Procedures; -- Create the table. CREATE TABLE Proc_Test.Test_Procedures ( Last_Update TIMESTAMP, Sequence FLOAT NOT NULL PRIMARY KEY, Description TEXT, Command_Line TEXT NOT NULL, Success_Status TEXT, Success_Message TEXT, Time_Limit TEXT, On_Failure TEXT ); INSERT INTO Proc_Test.Test_Procedures ( Sequence, Description, Command_Line, Success_Status, Success_Message, Time_Limit, On_Failure ) VALUES ( 0.0, 'Normal', 'exit_status 0', '0', NULL, NULL, 'echo Failed with status ${Completion_Number}' ); INSERT INTO Proc_Test.Test_Procedures ( Sequence, Description, Command_Line, Success_Status, Success_Message, Time_Limit, On_Failure ) VALUES ( 1.0, 'Conditional status, should succeed', 'exit_status 3', '${Completion_Number} > ${Sequence}', NULL, NULL, 'echo Failed with status ${Completion_Number}' ); INSERT INTO Proc_Test.Test_Procedures ( Sequence, Description, Command_Line, Success_Status, Success_Message, Time_Limit, On_Failure ) VALUES ( 5.0, 'Conditional status, should succeed on even Source_Number not a multiple of 8', 'exit_status 0', '(${Source_Number} - (2 * trunc (${Source_Number} / 2)) = 0) & (${Source_Number} - (8 * trunc (${Source_Number} / 8)) <> 0)', NULL, NULL, 'echo Failed with status ${Completion_Number}' ); INSERT INTO Proc_Test.Test_Procedures ( Sequence, Description, Command_Line, Success_Status, Success_Message, Time_Limit, On_Failure ) VALUES ( 4.0, 'Database field reference test', 'echo {${Sources_Table}.Conductor_ID:Source_Number=${Source_Number}}', '0', NULL, NULL, 'echo Failed with status ${Completion_Number}' ); pirl-2.3.8/PIRL/Conductor/tests/noop.c0000644000175000017500000000007307673151655017352 0ustar mathieumathieu/* noop */ main () { //printf ("I'm noop!\n"); exit (0); } pirl-2.3.8/PIRL/Conductor/tests/initialize_tables.psql0000755000175000017500000001007511742733134022623 0ustar mathieumathieu#!/bin/csh # # Initialize Conductor pipeline tables using psql. # # CVS initialize_tables.psql,v 1.4 2012/04/16 06:04:12 castalia Exp # # Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the # Planetary Image Research Laboratory, Lunar and Planetary Laboratory at # the University of Arizona. # # This file is part of the PIRL Java Packages. # # The PIRL Java Packages are free software; you can redistribute them # and/or modify them under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # The PIRL Java Packages are distributed in the hope that they will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Defaults. set Initialize = 0 @ Default_Records = 16 set Default_Procedures_SQL = Proc_Test.Test_Procedures-Create.PSQL set Default_Sources_SQL = Proc_Test.Test_Sources-Create.PSQL # Command line parsing. while ($#argv) switch ($1) case -[Ii]*: set Initialize = 1 shift breaksw case -[Rr]*: shift if (! $#argv) then echo "Missing record count." goto Help endif @ Records = $1 if ($Records < 0) then echo "The records count can not be negative." goto Help endif shift breaksw case -[Pp]*: shift if (! $#argv) then echo "Missing Procedures SQL filename." goto Help endif set Procedures_SQL = $1 shift breaksw case -[Ss]*: shift if (! $#argv) then echo "Missing Sources SQL filename." goto Help endif set Sources_SQL = $1 shift breaksw default: echo "Unknown option: $1" case -{Hh}*: Help: echo "Usage: $0 " echo " Initialize Conductor pipeline tables using psql." echo " The 'test' catalog will be used." echo " Options -" echo " -Initialize_Sources" echo " Replace the Sources table before adding records." echo " Default: Add records to an existing table." echo " -Records " echo " Add number records to the Sources table." echo " Default: $Default_Records" echo " -Procedures_SQL " echo " Use the SQL commands file at pathname to create the Procedures table." echo " Default: $Default_Procedures_SQL" echo " -Sources_SQL " echo " Use the SQL commands file at pathname to create the Sources table," echo " if it does not already exist, and add one source record." echo " Default: $Default_Sources_SQL" echo " -Help" echo " Print this help message." exit 1 endsw end # Confirm the SQL files. if (! $?Procedures_SQL) set Procedures_SQL = $Default_Procedures_SQL if (! -f $Procedures_SQL) then echo "Could not find the Procedures SQL file: $Procedures_SQL" goto Help endif if (! $?Sources_SQL) set Sources_SQL = $Default_Sources_SQL if (! -f $Sources_SQL) then echo "Could not find the Sources SQL file: $Sources_SQL" goto Help endif # Procedures table. Always reinitialized. echo "Initializing the Procedures table:" echo "psql test < $Procedures_SQL" psql test < $Procedures_SQL if ($status) goto Failed # Sources table. Conditionally reinitialized. if ($Initialize) then echo "Removing the Sources table if it exists:" echo 'psql test -c "DROP TABLE IF EXISTS Proc_Test.Test_Sources;"' psql test -c 'DROP TABLE IF EXISTS Proc_Test.Test_Sources;' if ($status) goto Failed endif # Sources records. if (! $?Records) set Records = $Default_Records if ($Records) then echo "Adding $Records records to the Sources table:" if ($Records > 1) then echo "psql test < $Sources_SQL (repeated)" else echo "psql test < $Sources_SQL" endif while ($Records) psql test < $Sources_SQL if ($status) goto Failed @ Records-- end endif echo "The database operations succeeded." exit 0 Failed: echo "The database operation failed." exit 2 pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Sources-Create.PSQL0000644000175000017500000000111511052153223024431 0ustar mathieumathieu-- Proc_Test.Test_Sources-Create.SQL -- -- PIRL CVS ID: Proc_Test.Test_Sources-Create.PSQL,v 1.1 2008/08/18 01:55:31 castalia Exp -- Create the catalog if it doesn't exist. CREATE SCHEMA Proc_Test; -- Create the table if it doesn't exist. CREATE TABLE Proc_Test.Test_Sources ( Source_Number SERIAL NOT NULL UNIQUE PRIMARY KEY, Source_Pathname TEXT NOT NULL, Source_ID VARCHAR(64), Conductor_ID VARCHAR(64), Status TEXT, Log_Pathname TEXT, Last_Update TIMESTAMP ); -- Insert a sample record. INSERT INTO Proc_Test.Test_Sources (Source_Pathname) VALUES ('test_source'); pirl-2.3.8/PIRL/Conductor/tests/Clocker0000755000175000017500000000043507673151655017545 0ustar mathieumathieu#!/bin/csh if ($#argv >= 1) then set RATE = $1 if ($RATE <= 0) then eecho '\!\!\! clock rate must be > 0' exit -1 endif else set RATE = 1 endif if ($#argv >= 2) then @ CYCLES = $2 else @ CYCLES = -1 endif while ($CYCLES != 0) date sleep $RATE @ CYCLES-- end exit $RATE pirl-2.3.8/PIRL/Conductor/tests/test_String_Vector_Comparator.java0000644000175000017500000000733411742733134025111 0ustar mathieumathieu/* test_String_Vector_Comparator PIRL CVS ID: test_String_Vector_Comparator.java,v 1.4 2012/04/16 06:04:12 castalia Exp Test of the String_Vector_Comparator class. Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Conductor.String_Vector_Comparator; import java.util.Vector; import java.util.Iterator; import java.util.Collections; public class test_String_Vector_Comparator { public static void main ( String[] args ) { System.out.println ("*** test_String_Vector_Comparator\n" + String_Vector_Comparator.ID); Vector> sandwiches = new Vector> (); Vector sandwich; sandwich = new Vector (); sandwich.add (new Integer (12)); // Units sandwich.add ("Ham"); // Meat sandwich.add ("Provolone"); // Cheese sandwich.add ("Wheat"); // Bread sandwich.add (new Float (3.55)); // Price sandwiches.add (sandwich); sandwich = new Vector (); sandwich.add (new Integer (5)); sandwich.add ("Turkey"); sandwich.add ("Provolone"); sandwich.add ("White"); sandwich.add (new Float (2.00)); sandwiches.add (sandwich); sandwich = new Vector (); sandwich.add (new Integer (76)); sandwich.add ("Bologna"); sandwich.add ("American"); sandwich.add ("White"); sandwich.add (new Float (1.55)); sandwiches.add (sandwich); sandwich = new Vector (); sandwich.add (new Integer (16)); sandwich.add ("Tuna"); sandwich.add ("--"); sandwich.add ("Rye"); sandwich.add (new Float (1.50)); sandwich = new Vector (); sandwich.add (new Integer (8)); sandwich.add ("Roast Beef"); sandwich.add ("Provolone"); sandwich.add ("Pumpernickel"); sandwich.add (new Float (5.95)); sandwiches.add (sandwich); sandwich = new Vector (); sandwich.add (new Integer (1)); sandwich.add ("Salami"); sandwich.add ("Gouda"); sandwich.add ("Croissant"); sandwich.add (new Float (5.75)); sandwiches.add (sandwich); System.out.println ("Original Vector -"); print_table (sandwiches); String_Vector_Comparator test_comparator = new String_Vector_Comparator (); Collections.sort (sandwiches,test_comparator); System.out.println ("Sorted on field 0 -"); print_table (sandwiches); test_comparator.Index (1); Collections.sort (sandwiches,test_comparator); System.out.println ("Sorted on field 1 -"); print_table (sandwiches); test_comparator.Index (2); Collections.sort (sandwiches,test_comparator); System.out.println ("Sorted on field 2 -"); print_table (sandwiches); test_comparator.Index (3); Collections.sort (sandwiches,test_comparator); System.out.println ("Sorted on field 3 -"); print_table (sandwiches); test_comparator.Index (4); Collections.sort (sandwiches,test_comparator); System.out.println ("Sorted on field 4 -"); print_table (sandwiches); } private static void print_table ( Vector> table ) { Iterator entries = table.iterator (); while (entries.hasNext ()) System.out.println (entries.next ()); } } pirl-2.3.8/PIRL/Conductor/tests/test_source0000644000175000017500000000026107673151655020514 0ustar mathieumathieuTest source line 1 XXXXXXXXXX Test source line 2 .......... Test source line 3 MMMMMMMMMM Test source line 4 iiiiiiiiii Test source line 5 Test source line 6 Test source line 7 pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Procedures-Create.MySQL,template0000644000175000017500000000145110336723542027135 0ustar mathieumathieu# Proc_Test.Test_Procedures-Create.SQL,template # # PIRL CVS ID: Proc_Test.Test_Procedures-Create.MySQL,template,v 1.2 2005/11/16 21:40:50 castalia Exp # Create the catalog if it doesn't exist. CREATE DATABASE IF NOT EXISTS Proc_Test; # Drop the table if it exists. DROP TABLE IF EXISTS Proc_Test.Test_Procedures; # Create the table. CREATE TABLE Proc_Test.Test_Procedures ( Last_Update TIMESTAMP, Sequence FLOAT NOT NULL PRIMARY KEY, Description TEXT, Command_Line TEXT NOT NULL, Success_Status TEXT, Success_Message TEXT, Time_Limit TEXT, On_Failure TEXT ); INSERT INTO Proc_Test.Test_Procedures SET Sequence = , Description = , Command_Line = , Success_Status = , Success_Message = , Time_Limit = , On_Failure = ; pirl-2.3.8/PIRL/Conductor/tests/initialize_tables.mysql0000755000175000017500000000776711742733134023027 0ustar mathieumathieu#!/bin/csh # # Initialize Conductor pipeline tables using mysql. # # CVS initialize_tables.mysql,v 1.7 2012/04/16 06:04:12 castalia Exp # # Copyright (C) 2005-2012 Arizona Board of Regents on behalf of the # Planetary Image Research Laboratory, Lunar and Planetary Laboratory at # the University of Arizona. # # This file is part of the PIRL Java Packages. # # The PIRL Java Packages are free software; you can redistribute them # and/or modify them under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # The PIRL Java Packages are distributed in the hope that they will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # # Defaults. set Initialize = 0 @ Default_Records = 16 set Default_Procedures_SQL = Proc_Test.Test_Procedures-Create.MySQL set Default_Sources_SQL = Proc_Test.Test_Sources-Create.MySQL # Command line parsing. while ($#argv) switch ($1) case -[Ii]*: set Initialize = 1 shift breaksw case -[Rr]*: shift if (! $#argv) then echo "Missing record count." goto Help endif @ Records = $1 if ($Records < 0) then echo "The records count can not be negative." goto Help endif shift breaksw case -[Pp]*: shift if (! $#argv) then echo "Missing Procedures SQL filename." goto Help endif set Procedures_SQL = $1 shift breaksw case -[Ss]*: shift if (! $#argv) then echo "Missing Sources SQL filename." goto Help endif set Sources_SQL = $1 shift breaksw default: echo "Unknown option: $1" case -{Hh}*: Help: echo "Usage: $0 " echo " Initialize Conductor pipeline tables using mysql." echo " Options -" echo " -Initialize_Sources" echo " Replace the Sources table before adding records." echo " Default: Add records to an existing table." echo " -Records " echo " Add number records to the Sources table." echo " Default: $Default_Records" echo " -Procedures_SQL " echo " Use the SQL commands file at pathname to create the Procedures table." echo " Default: $Default_Procedures_SQL" echo " -Sources_SQL " echo " Use the SQL commands file at pathname to create the Sources table," echo " if it does not already exist, and add one source record." echo " Default: $Default_Sources_SQL" echo " -Help" echo " Print this help message." exit 1 endsw end # Confirm the SQL files. if (! $?Procedures_SQL) set Procedures_SQL = $Default_Procedures_SQL if (! -f $Procedures_SQL) then echo "Could not find the Procedures SQL file: $Procedures_SQL" goto Help endif if (! $?Sources_SQL) set Sources_SQL = $Default_Sources_SQL if (! -f $Sources_SQL) then echo "Could not find the Sources SQL file: $Sources_SQL" goto Help endif # Procedures table. Always reinitialized. echo "Initializing the Procedures table:" echo "mysql < $Procedures_SQL" mysql < $Procedures_SQL if ($status) goto Failed # Sources table. Conditionally reinitialized. if ($Initialize) then echo "Removing the Sources table if it exists:" echo 'mysql -e "DROP TABLE IF EXISTS Proc_Test.Test_Sources"' mysql -e "DROP TABLE IF EXISTS Proc_Test.Test_Sources" if ($status) goto Failed endif # Sources records. if (! $?Records) set Records = $Default_Records if ($Records) then echo "Adding $Records records to the Sources table:" if ($Records > 1) then echo "mysql < $Sources_SQL (repeated)" else echo "mysql < $Sources_SQL" endif while ($Records) mysql < $Sources_SQL if ($status) goto Failed @ Records-- end endif echo "The database operations succeeded." exit 0 Failed: echo "The database operations failed." exit 2 pirl-2.3.8/PIRL/Conductor/tests/test_procedure0000755000175000017500000000450307671730136021205 0ustar mathieumathieu#!/bin/perl # test_procedure # # test_procedure,v 1.4 2003/06/11 22:42:38 castalia Exp # This program, which takes a filename from the command line, is designed # to help test and exercise Conductor's treatment of stderr and stdout and # its different ways to gauge success. use English; use Getopt::Long; use strict; my ($Command) = $0; # Set autoflush to true so that stdout is flushed with each print, printf, # and write. $| = 1; # Process the command line. $GetOpt::Long::ignorecase = 1; my ($STATUS, $MESSAGE, $ID) = (undef, undef, undef); GetOptions ( "s|status=i" => \$STATUS, "m|message=s" => \$MESSAGE, "i|id=s" => \$ID ) or die "Usage: $0 [-status status] [-message message] [-id id] \n"; # Get the file name from the command line and open for reading. my $filename = $ARGV[0] or die "$Command: Filename required.\n$!"; $Command .= "($ID)" if $ID; # Print the file to stdout. open (INPUT_FILE, "< $filename") or die "$Command: Can't read $filename -\n$!"; print STDOUT "$Command O1: stdout $filename -\n"; while () {print STDOUT "$Command O1 - $_";} close (INPUT_FILE); # Print it to stderr. open (INPUT_FILE, "< $filename") or die "$Command: Can't read $filename -\n$!"; print STDERR "$Command E1: stderr $filename -\n"; while () {print STDERR "$Command E1 - $_";} close (INPUT_FILE); # Print it to both. open (INPUT_FILE, "< $filename") or die "$Command: Can't read $filename -\n$!"; print STDOUT "$Command O2: stdout $filename interleaved with stderr -\n"; print STDERR "$Command E2: stderr $filename interleaved with stdout -\n"; while () { print STDOUT "$Command O2 - $_"; print STDERR "$Command E2 - $_"; } close (INPUT_FILE); END { # The END block is reached on a die, so this code will be executed # even if there was an error. If $STATUS was successfully set by # the Getopt, then that will be reported. Otherwise, Perl's own # inherent exit statuses will be used. These seem to be the following: # # 0 No error. # # 2 Could not open the file. # # 255 Error in processing the arguments. if ($MESSAGE) { print STDOUT "$Command: Message follows -\n"; print STDOUT $MESSAGE . "\n"; } if ($STATUS) { print STDOUT "$Command: STATUS = $STATUS\n"; $? = $STATUS; } print STDOUT "$Command: stdout end.\n"; print STDERR "$Command: stderr end.\n"; } pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Procedures-Create.MySQL0000644000175000017500000000713311062356124025323 0ustar mathieumathieu# Proc_Test.Test_Procedures-Create.SQL,endless # # SQL (MySQL) commands to create a Procedures table for Conductor # testing. # # A Proc_Test catalog is created if it does not already exist. In this # catalog a Test_Procecedures table is created replacing a table of the # same name if it already exists. The table is then popluated with 5 # Conductor procedure definition records: # # 0 - exit_status 1 # Should always succeed. # The trivial exit_status program source code is provided. # 1 - exit_status 12 # Should succeed on even Source_Number not a multiple of 8. # A single Conductor test starting with Source_Number 1 will # result in Source_Number 1, 3, 5, 7, 8, 9 ... failing. Thus # for sequential failure stop set to 3 processing will stop at # Source_Number 9. # 2.{1-3} - Pipeline_Source -catalog ${Catalog} -pipeline ${Pipeline} # -config ${Configuration_Source} test_source # Add three test_source files to the pipline Sources table.. # This should ensure that uprocessed source records will always # be available so the test can run endlessly. # # This file can be used with the initialize_tables script. # # # PIRL CVS ID: Proc_Test.Test_Procedures-Create.MySQL,v 1.6 2008/09/12 03:04:52 castalia Exp # Create the catalog if it doesn't exist. CREATE DATABASE IF NOT EXISTS Proc_Test; # Drop the table if it exists. DROP TABLE IF EXISTS Proc_Test.Test_Procedures; # Create the table. CREATE TABLE Proc_Test.Test_Procedures ( Last_Update TIMESTAMP, Sequence FLOAT NOT NULL PRIMARY KEY, Description TEXT, Command_Line TEXT NOT NULL, Success_Status TEXT, Success_Message TEXT, Time_Limit TEXT, On_Failure TEXT ); INSERT INTO Proc_Test.Test_Procedures SET Sequence = 0, Description = 'Exit with status 1', Command_Line = 'exit_status 1', Success_Status = '${Sequence} + 1', Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Exit status ${Completion_Number} when 1 was expected'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 1, Description = 'Conditional status, should succeed on even Source_Number not a multiple of 8', Command_Line = 'exit_status 2', Success_Status = '(${Source_Number} - (2 * trunc (${Source_Number} / 2)) = 0) & (${Source_Number} - (8 * trunc (${Source_Number} / 8)) <> 0)', Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Failed with status ${Completion_Number}'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 2.1, Description = 'Add a source', Command_Line = 'Pipeline_Source -catalog ${Catalog} -pipeline ${Pipeline} -config ${Configuration_Source} test_source', Success_Status = 0, Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Pipeline_Source exit status ${Completion_Number} when 0 was expected'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 2.2, Description = 'Add another source', Command_Line = 'Pipeline_Source -catalog ${Catalog} -pipeline ${Pipeline} -config ${Configuration_Source} test_source', Success_Status = 0, Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Pipeline_Source exit status ${Completion_Number} when 0 was expected'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 2.3, Description = 'Add a third source', Command_Line = 'Pipeline_Source -catalog ${Catalog} -pipeline ${Pipeline} -config ${Configuration_Source} test_source', Success_Status = 0, Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Pipeline_Source exit status ${Completion_Number} when 0 was expected'; pirl-2.3.8/PIRL/Conductor/tests/test_Reference_Resolver.java0000644000175000017500000003333411742733134023710 0ustar mathieumathieu/* test_Reference_Resolver PIRL CVS ID: test_Reference_Resolver.java,v 1.19 2012/04/16 06:04:12 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import java.util.Vector; import java.util.Iterator; import java.text.ParseException; import PIRL.Conductor.Reference_Resolver; import PIRL.Conductor.Unresolved_Reference; import PIRL.Database.Database; import PIRL.Database.Database_Exception; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.PVL_Exception; public class test_Reference_Resolver { private static final String CONFIG_FILENAME = "Conductor.conf", TEST_CATALOG = "test", TEST_TABLE = TEST_CATALOG + '.' + "test_table"; private static int Tests_Total = 0, Tests_Passed = 0; public static void main ( String[] args ) { System.out.println ("*** test_Reference_Resolver: " + Reference_Resolver.ID); String server = null, config_filename = null; boolean Verbose = false; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].charAt (1)) { case 'C': case 'c': if (config_filename != null) { System.out.println ("Only one config filename may be specified."); System.exit (1); } if (++count == args.length || args[count].charAt (0) == '-') { System.out.println ("Missing config filename."); System.exit (1); } config_filename = args[count]; break; case 'V': case 'v': Verbose = true; break; default: System.out.println ("Unknown option: " + args[count]); case 'H': case 'h': System.out.println ("Usage: java test_Reference_Resolver [-Help] [-Verbose] [-Config filename] [server]"); System.exit (1); } } else { if (server != null) { System.out.println ("Only one server may be specified."); System.exit (1); } server = args[count]; } } // Construct a new Configuration. if (config_filename == null) config_filename = CONFIG_FILENAME; Configuration configuration = null; try {configuration = new Configuration (config_filename);} catch (Configuration_Exception exception) { System.out.println ("Unable to construct the Configuration.\n" + exception.getMessage ()); System.exit (1); } // Construct a new Database and connect to it. Database database = null; try {database = new Database (configuration);} catch (Database_Exception exception) { System.out.println ("Unable to construct the Database.\n" + exception.getMessage ()); System.exit (1); } try {database.Connect (server);} catch (Database_Exception exception) { System.out.println ("Unable to Connect to the Database.\n" + exception.getMessage ()); System.exit (1); } catch (Configuration_Exception exception) { System.out.println ("Unable to Connect to the Database.\n" + exception.getMessage ()); System.exit (1); } // Construct a new Reference_Resolver. Reference_Resolver resolver = new Reference_Resolver (database); int Status = 1; try { String description, pattern, expected, obtained; // Unresolved reference. pattern = "${Test_Parameter}"; try { expected = "Unresolved_Reference"; obtained = resolver.Resolve (pattern); } catch (Unresolved_Reference exception) { expected = obtained = exception.getMessage (); } Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); expected = "Default Value"; resolver.Default_Value (expected); obtained = resolver.Resolve (pattern); Check ("resolver.Default_Value (\"" + expected + "\")", expected, obtained, Verbose); // Malformed pattern. pattern = "${Test_Parameter"; try { expected = "ParseException"; obtained = resolver.Resolve (pattern); } catch (ParseException exception) { expected = obtained = exception.getMessage (); } Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "$Test_}Parameter"; try { expected = "ParseException"; obtained = resolver.Resolve (pattern); } catch (ParseException exception) { expected = obtained = exception.getMessage (); } Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "{}"; try { expected = "ParseException"; obtained = resolver.Resolve (pattern); } catch (ParseException exception) { expected = obtained = exception.getMessage (); } Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "{..:}"; try { expected = "ParseException"; obtained = resolver.Resolve (pattern); } catch (ParseException exception) { expected = obtained = exception.getMessage (); } Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Valid parameter. if (Verbose) System.out.println ("--- configuration.Set (\"Test_Parameter\", \"" + expected + "\");"); expected = "Test"; configuration.Set ("Test_Parameter", expected); pattern = "${Test_Parameter}"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Multiple parameter references. pattern = "${Test_Parameter}-${Test_Parameter}"; expected = "Test-Test"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Nested parameter references. pattern = "${${Test_Parameter}_Parameter}"; expected = "Test"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Parameter reference resolves to parameter reference. if (Verbose) System.out.println ("--- configuration.Set " +"(\"Test_Parameter\", \"${Rescan_Parameter}\");"); configuration.Set ("Test_Parameter", "${Rescan_Parameter}"); if (Verbose) System.out.println ("--- configuration.Set " +"(\"Rescan_Parameter\", \"" + expected + "\");"); configuration.Set ("Rescan_Parameter", expected); pattern = "${Test_Parameter}"; expected = "Test"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Mulitiple parameter selection. if (Verbose) System.out.println ("--- configuration.Add " +"(new Parameter (\"Test_Parameter\").Value (\"Single Value\"));"); configuration.Add (new Parameter ("Test_Parameter").Value ("Single Value")); if (Verbose) System.out.println ("--- configuration.Add\n" +" (new Parameter (\"Test_Parameter\").Value\n" +" (new Value (\"0\").Add (\"1\").Add (\"2\")\n" +" .Add (new Value (\"10\").Add (\"20\").Add (\"30\"))\n" +" .Add(\"3\")));"); configuration.Add (new Parameter ("Test_Parameter").Value (new Value ("0").Add ("1").Add ("2") .Add (new Value ("10").Add ("20").Add ("30")) .Add ("3"))); if (Verbose) System.out.println ("---- configuration.Description ();\n" + configuration.Description ()); pattern = "${Test_Parameter@1}"; expected = "Single Value"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2}"; expected = "0"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[1]}"; expected = "1"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[3][2]}"; expected = "30"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[3][-1]}"; expected = "10,20,30"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@1[-1]}"; expected = "Single Value"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[-1]}"; expected = "0,1,2,10,20,30,3"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[2][-1]}"; expected = "2"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[${Test_Parameter@2[4]}][1]}"; expected = "20"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); pattern = "${Test_Parameter@2[ (${Test_Parameter@2[3][1]} / 10) + 1 ][1]}"; expected = "20"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Escaped characters. pattern = "\\$\\{Test_Parameter\\}"; expected = pattern; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // "Resolve_to_End_Reference" method for a parameter reference. pattern = "${Test_Parameter}"; expected = "Test_Parameter"; obtained = resolver.Resolve_to_End_Reference (pattern); Check ("resolver.Resolve_to_End_Reference (\"" + pattern + "\")", expected, obtained, Verbose); // "Resolve_to_End_Reference" method for a field reference. pattern = "{MOC.MGSC.FILE_SPECIFICATION_NAME:PRODUCT_ID = 'SPO-1-210/03'}"; expected = "MOC.MGSC.FILE_SPECIFICATION_NAME:PRODUCT_ID = 'SPO-1-210/03'"; obtained = resolver.Resolve_to_End_Reference (pattern); Check ("resolver.Resolve_to_End_Reference (\"" + pattern + "\")", expected, obtained, Verbose); // Setup the test table. if (Verbose) System.out.println ("--- database.Delete (\"" + TEST_TABLE + "\", (Vector)null);"); try {database.Delete (TEST_TABLE, (Vector)null);} catch (Database_Exception exception) {/* No such table expected. */} Vector fields = new Vector (), values = new Vector (); fields.add ("ID"); values.add ("INT PRIMARY KEY AUTO_INCREMENT NOT NULL"); fields.add ("VOLUME"); values.add ("INT"); fields.add ("NAME"); values.add ("VARCHAR(20)"); fields.add ("DESCRIPTION"); values.add ("VARCHAR(80)"); if (Verbose) System.out.println ("--- database.Create (\"" + TEST_TABLE + "\",\n\"" + fields + "\",\n\"" + values + "\");"); database.Create (TEST_TABLE, fields, values); values.clear (); values.add ("NULL"); // Auto_increment the ID. values.add (""); values.add (""); values.add (""); for (int count = 0; count < 5; count++) { values.set (1, new Integer (count)); values.set (2, "Volume_" + count + "/Name_" + count); values.set (3, "Description for entry " + count); if (Verbose) System.out.println ("--- database.Insert (\"" + TEST_TABLE + "\",\n\"" + fields + "\",\n\"" + values + "\");"); database.Insert (TEST_TABLE, fields, values); } if (Verbose) Print_Table (database.Select (TEST_TABLE)); // Valid field reference. pattern = "{" + TEST_TABLE + ".VOLUME:ID = 2}"; expected = "1"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); // Nested feild references. pattern = "{" + TEST_TABLE + ".DESCRIPTION:NAME = " + "'Volume_{" + TEST_TABLE + ".Volume:ID = 3}" + "/Name_{" + TEST_TABLE + ".Volume:ID = 3}'}"; expected = "Description for entry 2"; obtained = resolver.Resolve (pattern); Check ("resolver.Resolve (\"" + pattern + "\")", expected, obtained, Verbose); Status = 0; } catch (Configuration_Exception exception) { System.out.println ("!!!!: Configuration_Exception -\n" + exception.getMessage ()); } catch (Database_Exception exception) { System.out.println ("!!!!: Database_Exception -\n" + exception.getMessage ()); } catch (ParseException exception) { System.out.println ("!!!!: ParseException -\n" + exception.getMessage ()); } catch (PVL_Exception exception) { System.out.println ("!!!!: PVL_Exception -\n" + exception.getMessage ()); } catch (Unresolved_Reference exception) { System.out.println ("!!!!: Unresolved_Reference -\n" + exception.getMessage ()); } System.out.println ('\n' + "Total tests: " + Tests_Total + '\n' + "Passed tests: " + Tests_Passed); System.exit (Status); } public static void Print_Table ( Vector table ) { Iterator table_list = table.iterator (); while (table_list.hasNext ()) System.out.println (table_list.next ()); System.out.println (); } public static boolean Check ( String description, String expected, String obtained, boolean verbose ) { boolean matches = obtained.equals (expected); System.out.print ((matches ? "PASS" : "FAIL") + ": " + description); if (verbose || ! matches) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else System.out.println (" - " + obtained); Tests_Total++; if (matches) Tests_Passed++; return matches; } } pirl-2.3.8/PIRL/Conductor/tests/Proc_Test.Test_Procedures-Create.MySQL,exit_status0000644000175000017500000000322611015767534027704 0ustar mathieumathieu# Proc_Test.Test_Procedures-Create.SQL # # PIRL CVS ID: Proc_Test.Test_Procedures-Create.MySQL,exit_status,v 1.4 2008/05/24 10:35:08 castalia Exp # Create the catalog if it doesn't exist. CREATE DATABASE IF NOT EXISTS Proc_Test; # Drop the table if it exists. DROP TABLE IF EXISTS Proc_Test.Test_Procedures; # Create the table. CREATE TABLE Proc_Test.Test_Procedures ( Last_Update TIMESTAMP, Sequence FLOAT NOT NULL PRIMARY KEY, Description TEXT, Command_Line TEXT NOT NULL, Success_Status TEXT, Success_Message TEXT, Time_Limit TEXT, On_Failure TEXT ); INSERT INTO Proc_Test.Test_Procedures SET Sequence = 0.0, Description = 'Normal', Command_Line = 'exit_status 0', Success_Status = '0', Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Failed with status ${Completion_Number}'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 1.0, Description = 'Conditional status, should succeed', Command_Line = 'exit_status 3', Success_Status = '${Completion_Number} > ${Sequence}', Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Failed with status ${Completion_Number}'; INSERT INTO Proc_Test.Test_Procedures SET Sequence = 5.0, Description = 'Conditional status, should succeed on even Source_Number not a multiple of 8', Command_Line = 'exit_status 0', Success_Status = '(${Source_Number} - (2 * trunc (${Source_Number} / 2)) = 0) & (${Source_Number} - (8 * trunc (${Source_Number} / 8)) <> 0)', Success_Message = NULL, Time_Limit = NULL, On_Failure = 'echo Failed with status ${Completion_Number}'; pirl-2.3.8/PIRL/Conductor/Notify.java0000644000175000017500000007643511742733132017207 0ustar mathieumathieu/* Notify PIRL CVS ID: Notify.java,v 1.25 2012/04/16 06:04:10 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import PIRL.Utilities.Streams; import PIRL.Strings.String_Buffer; import java.util.*; import java.net.*; import java.io.*; /* Provides a more sophisticated alternative to the mailto URL mechanism. */ import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; /** Notify is a mechanism for delivering messages to destinations.

Each of Notify's destinations is represented by a {@link java.net.URI} address which specifies the recipient of the message and the protocol to be used to deliver it. The mailto URI, which specifies how to deliver an e-mail message, is typical. If the address does not specify a protocol, it is treated as a mailto URI. If, for any reason, a destination cannot be used during the delivery of the notice, that destination is added to a Vector of undeliverable destinations.

Notify's message consists of text that is delivered to each destination via a {@link java.net.URLConnection}. The message contents may be provided by a resource specifier which may be a local resource - either a system filename or path within an enclosing jar file - or an external resource via a URL. The message may also be specified directly on the command line. The size of the message may be limited to a maximum number of lines and the number of last lines to always be retained in the message body may also be controlled. This is particularly useful for preventing unexpectedly large message files from causing memory exhaustion while still delivering the beginning and end of the message.

For mailto deliveries a From and Reply-To address and a Subject line may be provided.

@author Christian Schaller - UA/PIRL @version 1.25 */ public class Notify { /*============================================================================== Constants */ // Class ID public static final String ID = "PIRL.Conductor.Notify (1.25 2012/04/16 06:04:10)"; /** Marker for a message source as opposed to a message string. */ public static final char FILENAME_MARKER = '@'; /** Exit status values. */ public static final int SUCCESS = 0, ILLEGAL_SYNTAX = 1, NO_DESTINATION = 2, CANNOT_READ_MESSAGE_FILE = 3; // The destination(s) of the notification message private Vector Destinations = new Vector (), Undeliverable = new Vector (); // The message to be delivered. private String Message = ""; private int Max_Message_Lines = 1000, Max_Last_Lines = 50, Total_Lines = 0; private Vector Last_Lines = new Vector (Max_Last_Lines); private static final String USERNAME = System.getProperty ("user.name"), NL = System.getProperty ("line.separator"); public static final String DEFAULT_SMTP_SERVER = "localhost"; private static Properties Mail_Props; static { Mail_Props = new Properties (); Mail_Props.put ("mail.smtp.host", DEFAULT_SMTP_SERVER); Mail_Props.put ("mail.from", USERNAME); } private String From = USERNAME, Reply_To = null, Subject = null; // Debug control private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_PROCESSING = 1 << 1, DEBUG_GET_STREAM = 1 << 2, DEBUG_MESSAGE = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Notify object with the specified destinations.

@param destinations The Vector of destinations to which the notification message will be delivered. */ public Notify ( Vector destinations ) {Destinations (destinations);} /** Constructs a Notify object with no destinations. */ public Notify () {} /*============================================================================== Message Header */ /** Sets the outgoing SMTP server hostname.

@param smtp_server The hostname of the outgoing SMTP server. The default hostname is {@link #DEFAULT_SMTP_SERVER}. @return This Notify object. */ public Notify SMTP_Server ( String smtp_server ) { Mail_Props.setProperty ("mail.smtp.host", smtp_server); return this; } /** Gets the current SMTP Server setting.

@return The SMTP hostname String */ public String SMTP_Server () {return Mail_Props.getProperty ("mail.smtp.host");} /** Gets the list of destinations to which the notice will be delivered.

Notify will attempt to deliver to each of these destinations on the next invocation of the {@link #Deliver()} method. A failed delivery will add that destination to the list of undeliverable destinations. @return the destinations list as a Vector @see #Undeliverable() */ public Vector Destinations () {return Destinations;} /** Sets the list of destinations to which the message will be delivered.

Redundant destination addresses are removed from the list.

Setting the destinations list has the side effect of resetting the list of undeliverable destinations. @param destinations The Vector of destination URIs. @return This Notify object. @see #Undeliverable() */ public Notify Destinations ( Vector destinations ) { Destinations = Remove_Redundancies (destinations); Undeliverable.clear (); return this; } /** Gets the list of destinations to which the notice could not be delivered.

Whenever a {@link #Deliver() Deliver}y cycle is done destinations to which the notice could not be delivered - for whatever reason - are added to a list of undeliverable destinations. @return The Vector of undeliverable destinations. @see #Deliver() */ public Vector Undeliverable () {return Undeliverable;} /** Gets the From address for e-mail deliveries of the notice.

The From address is not used for other types of deliveries. @return The From address String. */ public String From () {return From;} /** Sets the From address for e-mail deliveries of the notice.

The From address is not used for other types of deliveries. Furthermore, if the From address is null, it will not be used at all. In this case, the underlying Java e-mail delivery mechanism (the mailto URL handler) will attempt to use an appropriate From address based on the owner of the Notify process.

Note that there is no checking provided to ensure that the From address is a valid e-mail address. The address should be a regular e-mail address and not a mailto URL. @param from_address The From address String. @return This Notify object. */ public Notify From ( String from_address ) { if ((From = from_address) == null) From = USERNAME; Mail_Props.setProperty ("mail.from", From); return this; } /** Gets the Reply-To address for e-mail deliveries of the notice.

The Reply-To address is not used for other types of deliveries. @return The Reply-To address String (may be null). */ public String Reply_To () {return Reply_To;} /** Sets the Reply-To address for e-mail deliveries of the notice.

The Reply-To address is not used for other types of deliveries. Furthermore, if the Reply-To address is null, it will not be used at all.

Note that there is no checking provided to ensure that the Reply-To address is a valid e-mail address. The address should be a regular e-mail address and not a mailto URL. @param reply_to_address The Reply-To address String. @return This Notify object. */ public Notify Reply_To ( String reply_to_address ) { Reply_To = reply_to_address; return this; } /** Gets the Subject line for e-mail deliveries of the notice.

The Subject line is not used for other types of deliveries. @return The Subject line String. */ public String Subject () {return Subject;} /** Sets the Subject line for e-mail deliveries of the notice.

The Subject line is not used for other types of deliveries. Furthermore, if the Subject line is null, it will not be used at all. @param subject_line The Subject line String. @return This Notify object. */ public Notify Subject ( String subject_line ) { Subject = subject_line; return this; } /*============================================================================== Message Body */ /** Gets the maximum number of message lines to be delivered.

@return The maximum number of message lines to be delivered. */ public int Max_Message_Lines () {return Max_Message_Lines;} /** Sets the maximum number of message lines to be delivered.

@param max_lines The maximum number of message lines to be delivered. If this is less than or equal to 0, the {@link Integer#MAX_VALUE maximum integer value} will be used. If it is less than {@link #Max_Last_Lines()} then {@link #Max_Last_Lines()} will be lowered to the same value and there will be no initial message lines. @return This Notify object. */ public Notify Max_Message_Lines ( int max_lines ) { if (max_lines <= 0) Max_Message_Lines = Integer.MAX_VALUE; else { Max_Message_Lines = max_lines; if (Max_Last_Lines > max_lines) Max_Last_Lines = max_lines; } return this; } /** Gets the maximum number of message last lines to be delivered.

@return The maximum number of message last lines to be delivered. */ public int Max_Last_Lines () {return Max_Last_Lines;} /** Sets the maximum number of message last lines to be delivered.

@param max_lines The maximum number of message lines to be delivered. If this is less than 0, 0 will be used. If it is greater than {@link #Max_Message_Lines()} then {@link #Max_Message_Lines()} will be used and there will be no initial message lines. @return This Notify object. */ public Notify Max_Last_Lines ( int max_lines ) { if (max_lines < 0) max_lines = 0; else if (max_lines > Max_Message_Lines) Max_Last_Lines = Max_Message_Lines; else Max_Last_Lines = max_lines; return this; } /** Gets the message to be delivered.

The message is composed of two parts: The initial lines of the message and the last lines of the message. No more than {@link #Max_Message_Lines()} user provided lines - where a line is a sequence of characters terminated by a line separator - will be included. If this limit is exceeded additional lines will be dropped; however, the last {@link #Max_Last_Lines()} lines will always be included. In this case the initial lines will be separated from the last lines by a line of the form:

... [number> lines omitted]

where number> is number of user provided lines that were omitted from the message.

@return The message String. */ public String Message () { String the_message; if (Last_Lines.size () == 0) the_message = Message; else { // Add the last lines. StringBuffer message = new StringBuffer (Message); if (Total_Lines > Max_Message_Lines) message.append ("... [" + (Total_Lines - Max_Message_Lines) + " lines omitted]" + NL); Iterator lines = Last_Lines.iterator (); while (lines.hasNext ()) message.append ((String)lines.next ()); the_message = message.toString (); } return the_message; } /** Reads a message from a named source.

The source can be the name of a local file, the full pathname to that file, the name of a file contained within a jar that packages Notify, a file URL that points to a local file, or a URL that points to an external resource.

Only {@link #Max_Message_Lines()} lines - where a line is a sequence of characters terminated by a line separator - will be retained. The last {@link #Max_Last_Lines()} lines from the source will be saved separately so they will be included in the delivered message regardless of the total number of lines available from the source (@see #Message()).

@param source The name of the message file. If null an empty message is used. @param convert_escapes If true, any escape sequences in the message are converted to their character equivalents; otherwise the string is unchanged. @param append If true, the message is appended to any existing message; otherwise the any existing message is replaced. @return This Notify object. @throws IOException If an input stream cannot be obtained from the message source or if there is a problem reading from the input stream. @see Streams#Get_Stream(String) @see String_Buffer#escape_to_special() */ public Notify Message_Source ( String source, boolean convert_escapes, boolean append ) throws IOException { if (source == null) return this; if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Message_Source: " + source + NL +" Append - " + append +" Max message lines - " + Max_Message_Lines + NL +" Max last lines - " + Max_Last_Lines); InputStream message_stream = Streams.Get_Stream (source); if (message_stream == null) throw new IOException (ID + NL +"Unable to access the message input source - " + source); BufferedReader input = new BufferedReader (new InputStreamReader (message_stream)); if (! append) { // Clear the existing message. Total_Lines = 0; Message = ""; Last_Lines.clear (); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- Source lines -" + NL); String line; int lines_read = 0, last_line_threshold = Max_Message_Lines - Max_Last_Lines; while (input.ready ()) { // Get the next message line. line = input.readLine () + NL; if ((DEBUG & DEBUG_MESSAGE) != 0) ++lines_read; if (convert_escapes) // Expand any escape sequences. line = new String_Buffer (line).escape_to_special ().toString (); if (++Total_Lines > last_line_threshold) { // Add to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (line); } else { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.print (line); Message += line; } } if ((DEBUG & DEBUG_MESSAGE) != 0) { if (Last_Lines.size () != 0) { System.out.println ("--- Last lines -"); Iterator lines = Last_Lines.iterator (); while (lines.hasNext ()) System.out.print ((String)lines.next ()); } System.out.println ("--- Total lines:" + NL +" " + lines_read + " source lines read." + NL +" " + Total_Lines + " message lines, first." + NL +" " + Last_Lines.size () + " message lines, last." + NL +"<<< Message_Source"); } return this; } /** Reads a message from a named source.

Any escape sequences are converted and any existing message appended.

@param source The name of the message file. If null the message is set to the empty String. @return This Notify object. @throws IOException If an input stream cannot be obtained from the message source or if there is a problem reading from the input stream. @see #Message_Source(String, boolean, boolean) */ public Notify Message_Source ( String source ) throws IOException {return Message_Source (source, true, true);} /** Sets the message to be delivered.

@param message The message String. A null message is empty. @param convert_escapes If true, any escape sequences in the message are converted to their character equivalents; otherwise the string is unchanged. @param append If true, the message is appended to any existing message; otherwise an existing message is replaced. @return This Notify object. @see String_Buffer#escape_to_special() */ public Notify Message ( String message, boolean convert_escapes, boolean append ) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Message:" +" convert escapes " + convert_escapes +", append " + append); if (! append) { // Clear the existing message. Total_Lines = 0; Message = ""; Last_Lines.clear (); } if (message != null && message.length () != 0) { if (convert_escapes) message = new String_Buffer (message).escape_to_special ().toString (); Append_Message (message); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("<<< Message"); return this; } /** Sets the message to be delivered.

Any escape sequences are expanded. Any existing message is cleared.

@param message The message String to be delivered. A null message is empty. @return This Notify object. @see #Message(String, boolean, boolean) */ public Notify Message ( String message ) {return Message (message, true, false);} /** Append lines from a String to the Message.

@param string The String from which to extract lines. @return The string index of the last character (exclusive) used. This will be 0 if the Message is already full (Total_Lines >= Max_Message_Lines). It will be -1 if the entire string was used. */ private void Append_Message ( String string ) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Append_Message: " + Total_Lines + " total lines so far" + NL +" Max message lines - " + Max_Message_Lines + NL +" Max last lines - " + Max_Last_Lines); int last = 0, last_line_threshold = Max_Message_Lines - Max_Last_Lines, NL_length = NL.length (); if (Total_Lines < last_line_threshold) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- MESSAGE"); // Count message lines. for (last = string.indexOf (NL); last >= 0 && ++Total_Lines < last_line_threshold; last = string.indexOf (NL, last + NL_length)); if (last < 0) // Take the entire message. Message += string; else { // Include the final NL. last += NL_length; if (last == string.length ()) { Message += string; last = -1; } else Message += string.substring (0, last); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- MESSAGE: " + Total_Lines + " total lines"); } if (last >= 0) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- LAST LINES: " + Last_Lines.size ()); int start = last; for (last = string.indexOf (NL, start); last >= 0; last = string.indexOf (NL, start)) { // Add to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (string.substring (start, last += NL_length)); ++Total_Lines; start = last; } if (start != string.length ()) { // Add unterminated line to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (string.substring (start)); ++Total_Lines; } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- LAST LINES: " + Last_Lines.size ()); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("<<< Append_Message: " + Total_Lines + " total lines so far"); } /*============================================================================== Delivery methods */ /** Delivers the notice to each destination.

The list of undeliverable destinations (destinations for which, for whatever reason, the delivery could not be made) is cleared prior to delivery. @return The number of successful destinations. @see #Undeliverable() */ public int Deliver () { if ((DEBUG & DEBUG_PROCESSING) != 0) System.out.println (">>> Deliver:" + NL +" To: " + Destinations + NL +" From: " + From + NL +" Reply_To: " + Reply_To + NL +" Subject: " + Subject + NL +" Size of Destinations: " + Destinations.size ()); Undeliverable.clear (); Iterator list = Destinations.iterator (); while (list.hasNext ()) { String destination = (String)list.next (); URI uri = null; try {uri = new URI (destination);} catch (URISyntaxException e) { Undeliverable.add (destination); continue; } URL url = null; try {url = uri.toURL ();} catch (Exception e) { // Attempt it as a mailto: URL try {url = new URL ("mailto:" + destination);} catch (Exception e_2) { Undeliverable.add (destination); continue; } } Deliver_To (url); } if ((DEBUG & DEBUG_PROCESSING) != 0) System.out.println ("<<< Deliver"); return Destinations.size () - Undeliverable.size (); } /** Delivers the notice to each destination.

@param destinations The Vector of desinations. @return The number of successful destinations. @see #Deliver() */ public int Deliver ( Vector destinations ) { Destinations (destinations); return Deliver (); } /** Delivers the message to a single destination.

The destination email address is obtained from the URL. A {@link javax.mail.MimeMessage} is created from the obtained address. The email is then sent via a {@link javax.mail.Session} object created from the set of Mail_Props Properties. If the mail can not be sent for any reason, the destination is added to the list of undeliverable destinations along with the reason for the failure.

Depending on the type of URL, additional information may be written. For a mailto URL, for example, a To address, a From address, a Reply-To address, and a Subject line may be added. @param url The destination URL. @see #Deliver() @see #Undeliverable() */ private void Deliver_To ( URL url ) { if ((DEBUG & (DEBUG_PROCESSING | DEBUG_MESSAGE)) != 0) System.out.println (">>> Deliver_To: " + url + NL); if ((DEBUG & DEBUG_MESSAGE) != 0) { System.out.println (" To: " + url.getPath () + NL +" From: " + From + NL +((Reply_To != null) ? (" Reply-To: " + Reply_To + NL) : "") +((Subject != null) ? (" Subject: " + Subject + NL) : "") +" Message -" + NL + Message () + NL +"<<< Deliver_To"); return; } Session mail_session = Session.getInstance (Mail_Props, null); MimeMessage mail_message = new MimeMessage (mail_session); try { mail_message.setRecipients (MimeMessage.RecipientType.TO, url.getPath ()); mail_message.setFrom (); if (Reply_To != null) mail_message.setReplyTo (new InternetAddress [] {new InternetAddress (Reply_To)}); if (Subject != null) mail_message.setSubject (Subject); mail_message.setSentDate (new Date ()); mail_message.setContent (Message (), "text/plain"); Transport.send (mail_message); } catch (MessagingException exception) { String error_message = "Mail sent to " + url.getPath () + " failed." + NL + "Reason: " + exception.getMessage (); Undeliverable.add (error_message); } if ((DEBUG & (DEBUG_PROCESSING | DEBUG_MESSAGE)) != 0) System.out.println ("<<< Deliver_To"); } /*============================================================================== Helpers */ /** Removes redundant entries from a Vector. @param vector The Vector to be vetted. @return A copy of the vector without redundant entries. */ private Vector Remove_Redundancies ( Vector vector ) { if (vector == null || vector.isEmpty () || vector.size () == 1) return vector; Vector copy = new Vector (vector.size ()); Iterator entries = vector.iterator (); while (entries.hasNext ()) { Object object = entries.next (); if (! copy.contains (object)) copy.add (object); } return copy; } /*============================================================================== Application main */ /** Processes the command line arguments.

All error messages are written to stderr. If any destinations are undeliverable, they are listed.

The exit status will be one of:

  • 0 - SUCCESS
  • 1 - ILLEGAL_SYNTAX
  • 2 - NO_DESTINATION
  • 3 - CANNOT_READ_MESSAGE_FILE

@param args The array of command line arguments. @see #Usage(int) */ public static void main ( String[] args ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("*** " + ID + " ***"); Notify notifier = new Notify (); Vector destinations = new Vector (); String smtp_server = null, message = null, from_address = null, reply_to_address = null, subject_line = null; for (int index = 0; index < args.length; index++) { if (args[index].length () > 1 && args[index].charAt (0) == '-') { switch (args[index].charAt (1)) { case 'T': // To case 't': case 'D': // Destination case 'd': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing To address."); Usage (ILLEGAL_SYNTAX); } // Break down comma separated list. for (int from = 0, to = args[index].indexOf (',', from); from < args[index].length (); to = args[index].indexOf (',', from)) { if (to < 0) to = args[index].length (); if (from < to) destinations.add (args[index].substring (from, to)); from = ++to; } break; case 'F': // From case 'f': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing From address."); Usage (ILLEGAL_SYNTAX); } notifier.From (args[index]); break; case 'O': case 'o': if (++index == args.length || args[index].charAt(0) == '-') { System.err.println ("Missing Outgoing hostname."); Usage (ILLEGAL_SYNTAX); } notifier.SMTP_Server (args[index]); break; case 'R': // Reply-to case 'r': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing Reply-To address."); Usage (ILLEGAL_SYNTAX); } notifier.Reply_To (args[index]); break; case 'S': // Subject case 's': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing Subject line."); Usage (ILLEGAL_SYNTAX); } notifier.Subject (args[index]); break; case 'M': // Message case 'm': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing message."); Usage (ILLEGAL_SYNTAX); } if (args[index].length () > 1 && args[index].charAt (0) == FILENAME_MARKER) { try {notifier.Message_Source (args[index].substring (1));} catch (IOException exception) { System.err.println (exception.getMessage ()); System.exit (CANNOT_READ_MESSAGE_FILE); } } else notifier.Message (args[index]); break; case 'L': // Limit case 'l': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing message limit value."); Usage (ILLEGAL_SYNTAX); } int limit = 0; try {limit = Integer.parseInt (args[index]);} catch (NumberFormatException exception) { System.err.println ("Invalid limit value: " + args[index]); Usage (ILLEGAL_SYNTAX); } if (args[index - 1].indexOf ('L', 2) > 1 || args[index - 1].indexOf ('l', 2) > 1) notifier.Max_Last_Lines (limit); else notifier.Max_Message_Lines (limit); break; case 'V': // Version case 'v': System.out.println (ID); System.exit (SUCCESS); case 'H': // Help case 'h': Usage (SUCCESS); default: System.err.println ("Unrecognized option: " + args[index]); Usage (ILLEGAL_SYNTAX); } } else destinations.add (args[index]); } if (destinations.isEmpty ()) { System.err.println ("No To address provided."); Usage (NO_DESTINATION); } notifier.Destinations (destinations); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("Delivering -" + NL + "To " + destinations.size () + " Destinations: " + destinations + NL + "From: " + notifier.From () + NL + "Reply-To: " + notifier.Reply_To () + NL + "Subject: " + notifier.Subject () + NL + "Message:" + NL + notifier.Message ()); notifier.Deliver (); Vector undeliverable = notifier.Undeliverable (); if (! undeliverable.isEmpty ()) { System.err.println ("Undeliverable destination" + (undeliverable.size () > 1 ? "s" : "") + " -"); Iterator list = undeliverable.iterator (); while (list.hasNext ()) System.err.println (list.next ()); } System.exit (SUCCESS); } /** Prints the command line usage.

Usage: Notify <Switches>
  Switches -
    [-To] <address | URI>[,...]
    [-From <address>] (default: username)
    [-Reply-to <address>]
    [-Subject <subject line>]
    [-Message <message> | @<source>]
    [-Limit[_Last] <lines> (default: message 1000, last 50)
    [-Outgoing_server] <hostname> (default: localhost)
    [-Version
    [-Help

At least one destination (To) address must be provided. Each destination is treated as a URI. If the address does not specify a protocol, it is treated as a mailto URI. A comma separated list of addresses may be provided.

The From and Reply-to addresses are optional and only used in association with mailto destinations. By default the user's address, if available, will be used for the From address. If no Reply-to address is provided, none will be used.

The Subject line is optional and only used in association with mailto destinations. If none is provided, none will be used.

To message may be any text. If the message text begins with the {@link #FILENAME_MARKER} ('@') the text without the marker is interpreted as a file or URL resource specifiction. This may be a local resource, either a system filename or path within an enclosing jar file, or it may refer to an external resource via a URL.

Multiple messages may be specified. They will be concatenated in the order they occur on the command line.

The size of the message may be limited to a maximum number of lines. Excess lines will be dropped but the last lines of the message - the number may be specified - will be retained.

The hostname of an SMTP server to which the message will be transmitted may be specified. The default hostname is "localhost".

N.B.: Command line options are applied in the order in which they are encountered. Thus, for example, message limit values should be set before the message body is specified.

The -Version option causes the class {@link #ID} to be written to stdout followed by a System.exit.

N.B.: This method always results in a System.exit. @param exit_status The exit status code. The usage statement is written to the stdout if the exit_status is {@link #SUCCESS}, otherwise to stderr. */ public static void Usage ( int exit_status ) { String usage = "Usage: Notify" + NL +" [-To]

[...]" + NL +" [-From
] (default: username)" + NL +" [-Reply-to
]" + NL +" [-Subject ]" + NL +" [-Message | @]" + NL +" [-Limit[_Last] ] (default: message 1000, last 50)" + NL +" [-Outgoing_server ] (default: localhost)" + NL +" [-Version]" + NL +" [-Help]"; if (exit_status == SUCCESS) System.out.println (usage); else System.err.println (usage); System.exit (exit_status); } } pirl-2.3.8/PIRL/Conductor/Procedures_Table_Model.java0000644000175000017500000002706711742733132022276 0ustar mathieumathieu/* Procedures_Table_Model PIRL CVS ID: Procedures_Table_Model.java,v 1.5 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import javax.swing.table.DefaultTableModel; import java.util.Vector; /** A Procedures_Table_Model contains a table of Conductor procedure definition records.

@author Bradford Castalia, UA/PIRL @version 1.5 */ public class Procedures_Table_Model extends DefaultTableModel { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor..Procedures_Table_Model (1.5 2012/04/16 06:04:10)"; /** Flag that determines if the table cells are editable. */ public boolean Editable = false; /** The row designated as the currently active procedure definitiion of the table. */ public int Current_Row = -1; private Vector Snapshot_Names = new Vector (0), Snapshot_Data = new Vector (0); private static final String NL = System.getProperty ("line.separator"); /*============================================================================== Constructors */ /** Construct an empty Procedures_Table_Model. */ public Procedures_Table_Model () {} /** Construct a Procedures_Table_Model from a Vector of data record Vectors and a Vector of field names.

@param records A Vector of records; each record is a Vector of field value Strings any of which may be null. @param field_names A Vector of field_name Strings. @see #setDataVector(Vector, Vector) */ public Procedures_Table_Model ( Vector records, Vector field_names ) {setDataVector (records, field_names);} /** Construct a Procedures_Table_Model from an array of data record arrays and an array of field names.

@param records An array of records; each record is an array of field value Strings any of which may be null. @param field_names An array of field_name Strings. @see #setDataVector(Vector, Vector) */ public Procedures_Table_Model ( String[][] records, String[] field_names ) {setDataVector (convertToVector (records), convertToVector (field_names));} /** Construct a Procedures_Table_Model from a Vector of field names but no table data.

A {@link #Snapshot() snapshot} of the model is taken.

@param field_names A Vector of field_name Strings. */ public Procedures_Table_Model ( Vector field_names ) { super (field_names, 0); Snapshot (); } /** Construct a Procedures_Table_Model from a Vector of field names but no table data.

A {@link #Snapshot() snapshot} of the model is taken.

@param field_names An array of field_name Strings. */ public Procedures_Table_Model ( String[] field_names ) {this (convertToVector (field_names));} /*============================================================================== TableModel */ /** Get the Class for a table column.

@param column A table column index. @return String.class; except if the column is not a valid value Object.class is returned. */ public Class getColumnClass ( int column ) { if (column >= 0 && column < getColumnCount ()) return String.class; return Object.class; } /** Get a table cell value.

@param row A table row index. @param column A table column index. @return The Object at the row and column cell location of the table data. This will be null if either the row or column value is invalid. */ public Object getValueAt ( int row, int column ) { if (row < 0 || row >= getRowCount () || column < 0 || column >= getColumnCount ()) return null; return ((Vector)dataVector.get (row)).get (column); } /** Test if a table cell is editable.

@param row A table row index. @param column A table column index. @return The value of the {@link #Editable} flag. */ public boolean isCellEditable ( int row, int column ) {return Editable;} /*============================================================================== DefaultTableModel */ /** Replace the table data content.

Both the field names and procedure records are replaced.

A {@link #Snapshot() snapshot} of the model is taken.

@param records A Vector of records; each record is a Vector of field value Strings any of which may be null. @param field_names A Vector of field_name Strings. */ public void setDataVector ( Vector records, Vector field_names ) { super.setDataVector (records, field_names); Snapshot (); } /** Insert a procedure definition record in the table.

The table snapshot data is updated with the new record.

@param row A table row index. This must be in the range zero to the total number of records in the table, inclusive. If the row equals the total number of records in the table the record is appended to the table. @param record A procedure definition record Vector. It's size must be the same as the table's {@link #getColumnCount() column count}. */ public void insertRow ( int row, Vector record ) { super.insertRow (row, record); int columns = getColumnCount (); Vector current = (Vector)dataVector.get (row), original = new Vector (columns); for (int column = 0; column < columns; column++) original.add (current.get (column)); Snapshot_Data.insertElementAt (original, row); } // The addColumn and other insertRow methods defer to the one above. /*============================================================================== Accessors */ /** Get the procedure definition record at a table row index.

@param row A table row index. This must be in the range zero to the total number of records in the table, exclusive. @return A procedure definition record Vector. */ public Vector Record ( int row ) { if (row >= 0 && row < getRowCount ()) return (Vector)dataVector.get (row); return null; } /** Set the procedure definition record at a table row index.

If a new row is added to the table the table snapshot data is updated with the new record.

Notification of the row record being updated or added will be generated.

@param row A table row index. This must be in the range zero to the total number of records in the table, inclusive. If the row equals the total number of records in the table the record is appended to the table. Otherwise the record replaces the existing record at the row index. @param record A procedure definition record Vector. It's size must be the same as the table's {@link #getColumnCount() column count}. @return This Procedures_Table_Model. @throws ArrayIndexOutOfBoundsException If the row index is invalid or the record size is not the same as the number of columns in the table. */ public Procedures_Table_Model Record ( int row, Vector record ) { if (record == null) return this; if (row < 0 || row > getRowCount ()) throw new ArrayIndexOutOfBoundsException (ID + NL + "Unable to set the record at row " + row + '.' + NL + "The valid row range is 0 - " + getRowCount () + " inclusive."); if (record.size () != getColumnCount ()) throw new ArrayIndexOutOfBoundsException (ID + NL + "Unable to set the record at row " + row + '.' + NL + "The " + record.size () + " element record size does not match the " + getColumnCount () + " column table size."); if (row == getRowCount ()) insertRow (row, record); else { dataVector.set (row, record); fireTableRowsUpdated (row, row); } return this; } /** Restore the table content from the last {@link #Snapshot() snapshot}.

The table records are restored from a copy of the last {@link #Snapshot_Table_Data() snapshot table data}. If the number of table columns has changed since the last snapshot the field names are also restored from a copy of the last {@link #Snapshot_Column_Names() snapshot column names}. N.B.: No check is made if the field names have changed; full table content can be forced by:

    setDataVector
      (Table (Snapshot_Table_Data ()),
      Record (Snapshot_Column_Names ()));

Notification of the change to the table contents will be generated.

@see #Snapshot() */ public void Restore () { int rows = dataVector.size (), columns = columnIdentifiers.size (); dataVector = Table (Snapshot_Data); if (columns == Snapshot_Names.size ()) { if (rows == Snapshot_Data.size ()) fireTableRowsUpdated (0, rows - 1); else fireTableDataChanged (); } else { columnIdentifiers = Record (Snapshot_Names); fireTableStructureChanged (); } } /** Save a snapshot of the table contents.

Both the table data records and field names are saved.

@see #Snapshot_Table_Data() @see #Snapshot_Column_Names() */ public void Snapshot () { Snapshot_Names = Record (columnIdentifiers); Snapshot_Data = Table (dataVector); } /** The last table data records snapshot.

@return A Vector containing a copy of each record Vector in the table at the time of the last {@link #Snapshot() snapshot} was taken. N.B.: The returned Vector is the snapshot data, not a copy. */ public Vector Snapshot_Table_Data () {return Snapshot_Data;} /** The last table field names snapshot.

@return A Vector containing a copy of the field names for the table at the time of the last {@link #Snapshot() snapshot} was taken. N.B.: The returned Vector is the snapshot data, not a copy. */ public Vector Snapshot_Column_Names () {return Snapshot_Names;} /** The table field names.

@return A Vector containing the field names for the table. N.B.: The returned Vector is not a copy. */ public Vector Column_Names () {return columnIdentifiers;} /** Copy table data.

The table is a Vector of row record Vectors. The table is expected to be square: the size of the first row record determines the size of all other rows; no attempt is made to compensenate for an irregular table.

The data elements are not copied; the reference to each element Object is copied. For the expected case where the data elements are Strings, which are immutable, there is no concern that changes in the copied data will affect the copy, or vice versa.

@param records A Vector of records; each record is a Vector of field value Strings any of which may be null. @return A Vector containing a copy of each record Vector in the table. This will be null if the table is null. */ public static Vector Table ( Vector records ) { if (records == null) return null; int row = 0, rows = records.size (); Vector copy = new Vector (rows); while (row < rows) copy.add (Record ((Vector)records.get (row++))); return copy; } /** Copy record data.

@param record A Vector of field value Strings any of which may be null. @return A Vector containing a copy of the record. Must not be null. */ public static Vector Record ( Vector record ) { if (record == null) return null; int column = 0, columns = record.size (); Vector copy = new Vector (columns); while (column < columns) copy.add (record.get (column++)); return copy; } } pirl-2.3.8/PIRL/Conductor/Table_Mouse_Listener.java0000644000175000017500000000377411742733133022000 0ustar mathieumathieu/* Table_Mouse_Listener PIRL CVS ID: Table_Mouse_Listener.java,v 1.5 2012/04/16 06:04:11 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import javax.swing.JTable; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.Point; /** A Table_Mouse_Listener provides deselect capability for table selections.

If a single mouse click of button one with a control key pressed occurs in a selected table column that column is deselected.

@author Bradford Castalia, UA/PIRL @version 1.5 */ public class Table_Mouse_Listener extends MouseAdapter { public void mouseClicked ( MouseEvent event ) { if (event.isControlDown () && event.getButton () == MouseEvent.BUTTON1 && event.getClickCount () == 1 && event.getComponent () instanceof JTable) { JTable table = (JTable)event.getComponent (); int column = table.getSelectedColumn (); if (column >= 0) { Point point = event.getPoint (); if (column == table.columnAtPoint (point) && table.rowAtPoint (point) >= 0) table.removeColumnSelectionInterval (column, column); } } } } pirl-2.3.8/PIRL/Conductor/Notify0000755000175000017500000000506011652360526016257 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the Notify Java application. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath or provided as the value of an environment # variable having the names listed here: # # JAVAMAIL - http://www.oracle.com/technetwork/java/javamail/ # JAF - http://java.sun.com/products/archive/javabeans/jaf11.html # # The pathnames to the JavaX JavaMail and JavaBeans Activation # Framework jar files or the root directory where their class files # are located. These packages provide email message sending # functionality as used by the Notify class. Note: These packages are # only required when Notify must use an "unfriendly" email server. # They may not be required at all, in which case a Notify-simple.java # implementation is provided that only uses JFC classes and worked # fine for many years until a "sophisticated" email server was # encountered. # # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # # CVS ID: Notify,v 1.7 2011/10/27 22:55:18 castalia Exp $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $JAVAMAIL = "$PIRL_JAVA_HOME/javamail/mail.jar" unless $JAVAMAIL = $ENV{"JAVAMAIL"}; Add_to_CLASSPATH ($JAVAMAIL); $JAF = "$PIRL_JAVA_HOME/jaf/activation.jar" unless $JAF = $ENV{"JAF"}; Add_to_CLASSPATH ($JAF); @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Conductor.Notify", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/package.html0000644000175000017500000000760311742733133017345 0ustar mathieumathieu The PIRL Conductor package provides an application to manage procedure pipelines.

A procedure pipeline is defined by a Database table of Procedures definition records paired with a table of file Sources records. Each file specified in a Sources record is processed in sequence by each procedure defined in a Procedures record. Procedure definitions include the relative order of the procedure in the pipeline, its command line to be executed, the successful exits status or message, the maximum amount of time a procedure will be allowed to run, and a command line to be executed if the procedure does not complete successfully. The Sources records include, in addition to the pathname of the file to be processed, a record of the completion status of each pipeline procedure applied to it.

Each source file processed is provided with a log file that is tied by a unique name to is Sources record. The log includes a detailed description of all procedures executed, their stdout and stderr output, and a record of how the procedure completed.

All procedure definitions may include embedded references to Database fields or Conductor configuration parameters. These are effectively variable names that are replaced with the values to which they resolve. A reference to a Database field resolves to the value of the field in the table of a catalog named in the reference for the record selected by a conditional expression. A reference to a configuration parameter resolves to the value of user specified parameter name in the current configuration file or any of the implicit parameters describing the current state of source processing. References may contain nested references and may resolve to values that are themselves references. This allows procedure definitions to be described in terms of variables that are dynamically resolved to specific values at the time of procedure execution.

Conductor manages the acquisition of Sources records for processing to ensure that only one Conductor will process each record, and the source will only be processed once unless specifically reset for reprocessing by some other agent. It is possible to set Sources to be (re)processed starting in the middle of the Procedures sequence. The exclusive control of a Sources record by one Conductor does not preclude multiple entries of the same file into a Sources table, if appropriate. It does enable more than one Conductor, on the same or separate host systems, to process the same pipeline at the same time; for example to safely distribute and parallelize processing.

Conductor offers a monitor mode that includes user controlled starting and stopping of processing and a terminal-like scrolling text display of all log output. Conductor can also be run silently for batch processing by manual initiation, as a cron job, or as a daemon that will constantly poll its pipeline for new Sources entries to be processed. @see PIRL.Database pirl-2.3.8/PIRL/Conductor/File_Exists0000755000175000017500000001142411742733132017223 0ustar mathieumathieu#!/usr/bin/perl =pod =head1 NAME B Repeated tests if a file exists. =head1 SYNOPSIS File_Exists EBE [-Repeat EBE] [-Delay EBE] [-Help] =head1 DESCRIPTION The pathname is tested to see if a file exits at that location. If it does the procedure exits with a success status. Otherwise the procedure sleeps for the number of delay seconds and repeats the test. This continues for up to the number of repeat tries or until the test succeeds. If the test never succeeds the procedure exits with a failure status. =head1 OPTIONS AND ARGUMENTS One pathname must be provided to test. =over =item -Repeat EBE The number of times to repeat the test for the existence of the file before giving up. The minimum number of repeat tries is 0; i.e. only test once and do not repeat the test. The default is 12 tries. =item -Delay EBE The number of seconds to delay between repeat tries. The default is 10 seconds. The minimum delay is 1 second and the maximum is 60 seconds. =item -Help The man page for this procedure is listed. =back =head1 Exit Status =over =item E<48> - Success A file was found to exist at the pathname. =item E<49> - Bad command line syntax A command line syntax usage message will be provided. =item E<50> - No such file No file was found to exist at the pathname. =back =head1 Author Bradford Castalia, UA/PIRL =head1 Copyright Copyright (C) 2009-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . =head1 Version 2.4 2012/04/16 06:04:10 =cut # CVS ID: File_Exists,v 2.4 2012/04/16 06:04:10 castalia Exp #=============================================================================== $CVS_ID = 'Pipeline_Source (2.4 2012/04/16 06:04:10)'; ($Command_Name = $0) =~ s|.*/(\S+)$|$1|; print "$Command_Name - $CVS_ID\n"; # Defaults: $Tries = 12; $Delay = 10; $MAX_DELAY = 60; # Exit status values: $SUCCESS = 0; $BAD_SYNTAX = 1; $NO_SUCH_FILE = 2; use File::Spec; #------------------------------------------------------------------------------- # Command line arguments: use Pod::Usage; pod2usage ( -verbose => 0, -exitval => $BAD_SYNTAX ) unless @ARGV; while ($option = shift @ARGV) { if ($option =~ /^-[Rr]/) { pod2usage ( -message => "$Command_Name: Missing repeat tries.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $Tries = shift @ARGV; pod2usage ( -message => "$Command_Name: The repeat tries \"$Tries\" is not a number.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($Tries =~ /\D/); $Tries = 0 if ($Tries < 0); next; } if ($option =~ /^-[Dd]/) { pod2usage ( -message => "$Command_Name: Missing delay seconds.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $Delay = shift @ARGV; pod2usage ( -message => "$Command_Name: The delay seconds \"$Delay\" is not a number.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($Delay =~ /\D/); $Delay = 1 if ($Delay <= 0); $Delay = $MAX_DELAY if ($Delay > $MAX_DELAY); next; } # Help. pod2usage ( -verbose => 2, -exitval => $SUCCESS ) if ($option =~ /^-[Hh]/); pod2usage ( -message => "$Command_Name: Unknown option \"$option\"\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($option =~ /^-/); pod2usage ( -message => "$Command_Name: Multiple pathnames specified - \n" ."$Pathname\n" ."and\n" ."$option", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($Pathname && $Pathname ne $option); $Pathname = $option; } pod2usage ( -message => "$Command_Name: No pathname specified.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless $Pathname; # Ensure absolute pathname. $Pathname = File::Spec->rel2abs ($Pathname); do { if (-e "$Pathname") { print "A file exists at the $Pathname pathname.\n"; exit ($SUCCESS); } sleep ($Delay); } while ($Tries--); print "No file found at the $Pathname pathname.\n"; exit ($NO_SUCH_FILE); pirl-2.3.8/PIRL/Conductor/Pipeline_Configuration/0000755000175000017500000000000012052546525021514 5ustar mathieumathieupirl-2.3.8/PIRL/Conductor/Pipeline_Configuration/Makefile0000644000175000017500000000126011571626206023153 0ustar mathieumathieu# Makefile for Java Conductor.Pipeline_Configuration package. # # PIRL CVS ID: Makefile,v 1.4 2011/06/02 06:24:06 castalia Exp # gmake syntax # Installation location for the executable scripts. SCRIPTS_DIR ?= /opt/local/sh # Location of commons-cli classes Commons_CLI ?= /opt/java/commons-cli/commons-cli.jar JPATH ?= ../../..:$(Commons_CLI) JC ?= javac CLASSES := Pipeline_Configuration.class SCRIPTS := Pipeline_Configuration # Targets: all: classes classes: $(CLASSES) install: all cp -p $(SCRIPTS) $(SCRIPTS_DIR) clean: rm -f *.class .PHONY: all classes install clean .SUFFIXES: .java .class .java.class: $(JC) -classpath $(JPATH) $(JFLAGS) $< pirl-2.3.8/PIRL/Conductor/Pipeline_Configuration/Pipeline_Configuration0000755000175000017500000001403411742741741026102 0ustar mathieumathieu#!/usr/bin/perl # # Wrapper for the Pipeline_Configuration Java application. =pod =head1 NAME B Reads and writes pipeline configuration parameters stored in a database table. =head1 SYNOPSIS Pipeline_Configuration [options ...] Options: -catalog override the configuration catalog (useful for testing) -configuration configuration file -description write-mode: the description to write -help print this message -info show all available information for a parameter -parameter the parameter to query -pipeline the pipeline to use when querying for a parameter -quiet suppress extraneous output to simplify parameter value parsing -strict treat value validation failures as global errors -validation write-mode: the validation string to write -value write-mode: the value to write -write enable write-mode for updating or adding a pipeline configuration =head1 DESCRIPTION The following are examples of using B which includes shell transcriptions of (1) querying a pipeline's parameter for a value, (2) getting the full information of a parameter, and (3) writing information back to the catalog for the pipeline. 1. Querying a pipeline parameter value: The result of the execution of will give the value stored in the catalog. $ Pipeline_Configuration \ -configuration /HiRISE/Configuration/EDRgen/EDRgen.conf \ -pipeline HiColorNorm -parameter Make_Unfiltered_Cube Returns: TRUE 2. Getting all available information from the catalog for a given parameter: The result of the execution of will give a PVL-formatted string. $ Pipeline_Configuration \ -configuration ~/HiRISE/Configuration/EDRgen/EDRgen.conf \ -pipeline HiColorNorm -parameter Make_Unfiltered_Cube -info Returns: PIPELINE = HiColorNorm PARAMETER = Make_Unfiltered_Cube VALUE = TRUE VALIDATION_STRING = TRUE|FALSE DESCRIPTION = A boolean value to indicate if the HiColorNorm pipeline should create the unfiltered color cube that has not passed through the noise or furrow filtering corrections LAST_UPDATE = 2011-05-17 13:51:59.0 3. Writing a value and validation string to the catalog for the given pipeline: The result of this execution will be an exit code of 0 and no output. $ Pipeline_Configuration \ -configuration ~/HiRISE/Configuration/EDRgen/EDRgen.conf \ -pipeline HiColorNorm -parameter Make_Unfiltered_Cube -write \ -value FALSE -validation "TRUE|FALSE|true|false" Notice that for write operations the "-write" parameter must be given. =head1 Author Chris Van Horne, UA/HiROC =head1 Copyright Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . =head1 Version 1.7 2012/04/16 07:01:53 =cut # CVS ID: Pipeline_Configuration,v 1.7 2012/04/16 07:01:53 castalia Exp #=============================================================================== $CVS_ID = 'Pipeline_Configuration (1.7 2012/04/16 07:01:53)'; ($Command_Name = $0) =~ s|.*/(\S+)$|$1|; use Getopt::Long qw(:config ignore_case); my %h= (); GetOptions( \%h, "buildtable", "catalog=s", "configuration=s", "description=s", "help", "info", "parameter=s", "pipeline=s", "quiet", "strict", "table=s", "validation=s", "value=s", "write", ); if(!exists($h{"quiet"})) { print "$Command_Name - $CVS_ID\n"; } # find all nonlower command-line args and map them to lowercase for $nonlower (grep { lc($_) ne $_ } keys %h) { $lower = lc $nonlower; $h{$lower} = delete $h{$nonlower}; } # join lowercase options with a space-delimiter and quote the values $cmdline = join " ", map "-$_ \"$h{$_}\"", keys %h; $APPLICATION = "PIRL.Conductor.Pipeline_Configuration.Pipeline_Configuration"; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); $CLASSPATH = $ENV{"CLASSPATH"}; if($PIRL_JAVA_HOME) { if($CLASSPATH) { $CLASSPATH = "$PIRL_JAVA_HOME:$CLASSPATH" unless $CLASSPATH =~ /$PIRL_JAVA_HOME/; } else { $CLASSPATH = $PIRL_JAVA_HOME; } $MySQL_JDBC = "$PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar" unless $MySQL_JDBC = $ENV{"MySQL_JDBC"}; Add_to_CLASSPATH($MySQL_JDBC); $PostgreSQL_JDBC = "$PIRL_JAVA_HOME/PosgreSQL/postgresql.jar" unless $PostgreSQL_JDBC = $ENV{"PostgreSQL_JDBC"}; Add_to_CLASSPATH($PostgreSQL_JDBC); $COMMONS_CLI = "${PIRL_JAVA_HOME}/commons-cli/commons-cli.jar" unless $COMMONS_CLI = $ENV{"COMMONS_CLI"}; Add_to_CLASSPATH($COMMONS_CLI); } exec "java -client -cp ${CLASSPATH} ${APPLICATION} $cmdline"; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/Pipeline_Configuration/Pipeline_Configuration.java0000644000175000017500000006247411742741741027032 0ustar mathieumathieu/* Pipeline_Configuration PIRL CVS ID: Pipeline_Configuration.java,v 1.6 2012/04/16 07:01:53 castalia Exp Copyright (C) 2011-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor.Pipeline_Configuration; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Vector; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Database.Database; import PIRL.Database.Database_Exception; /** A Pipeline_Configuration provides an API and command-line tool for reading and writing pipeline configurations stored a database.

A Pipeline_Configuration database table has the following fields:

ID
A unique auto-incremented integer.
PIPELINE
A pipeline name.
PARAMETER
A pipeline configuration parameter. This corresponds to the command-line "pipeline" specification.
VALUE
The value of the configuration parameter.
VALIDATION_STRING
A regular expression used to validate the contents of the value.
DESCRIPTION
A short description of the parameter
LAST_UPDATE
The time when the record was last modified.

API

Pipeline_Configuration provides the ability to read and write all the columns except the ID. Typical usage of the API involves constructing a Pipeline_Configuration object with a database connection, catalog, pipeline, and whether strict-mode should be enforced. For example:

	Configuration
		configuration = new Configuration ();
	Database
		database = new Database (configuration);
	database.Connect ();
	String
		catalog = "HiRISE",
		pipeline = "HiColorNorm";
	boolean
		strict = true;
	Pipeline_Configuration
		pipeline_configuration = new Pipeline_Configuration
			(database.Connection (), catalog, pipeline, strict);

Where:

configuration
A PIRL.Configuration object that provides Database access information. A Database configuration file may be used for this purpose.
database
A PIRL.Database object.
catalog
The name of the database catalog that contains the table. By default this is obtained from the Configuration "Catalog" parameter.
pipeline
The name of the pipeline to use when reading or writing a database table record.
strict
When true all read operations will have their VALUE validated against the VALIDATION_STRING

The pipeline_configuration can then be used to read and write table records. For example:

	Conf
		conf = pipeline_configuration.read ("Make_Unfiltered_Cube");
	System.out.println ("Make_Unfiltered_Cube = " + conf.value);
	System.out.println ("Description: " + conf.description);

The {@link Conf} class is used to contain the field values of a table record.

A Pipeline_Configuration.write updates a table record if a named pipeline parameter is present in a table record. A new record is inserted in the table otherwise. Table record updates will only set field values for which data has supplied. For example:

	pipeline_configuration.write
		("Make_Unfiltered_Cube", "TRUE");
	pipeline_configuration.write
		("Make_Unfiltered_Cube", "TRUE", "TRUE|FALSE");
	pipeline_configuration.write
		("Make_Unfiltered_Cube", "TRUE", Validator.BOOLEAN);
	pipeline_configuration.write
		("Make_Unfiltered_Cube", "TRUE", "TRUE|FALSE", "A description.");

Each write will modify the same record (except, perhaps, the first which might insert a new record) that has the "HiColorNorm" value in the PIPELINE field and the "Make_Unfiltered_Cube" value in the PARAMETER field of a record of the default "Pipeline_Configuration" table in the "HiRISE" catalog.

Application

The command-line usage can be obtained by using the -help option:

Here is an example of using Pipeline_Configuration for querying a pipeline's parameter for a value, getting all parameter information, and writing parameter information for a pipeline.

Querying a pipeline parameter value:

Pipeline_Configuration \ 
	-configuration ~/HiRISE/Configuration/EDRgen/EDRgen.conf \ 
	-pipeline HiColorNorm \ 
	-parameter Make_Unfiltered_Cube

Returns:

TRUE

Getting all information for a parameter:

Pipeline_Configuration \ 
	-configuration ~/HiRISE/Configuration/EDRgen/EDRgen.conf \ 
	-pipeline HiColorNorm \ 
	-parameter Make_Unfiltered_Cube \ 
	-info

Returns:

PIPELINE = HiColorNorm
PARAMETER = Make_Unfiltered_Cube
VALUE = TRUE
VALIDATION_STRING = TRUE|FALSE
DESCRIPTION = A boolean value to indicate if the HiColorNorm pipeline
			  should create the unfiltered color cube that has not passed
			  through the noise or furrow filtering corrections
LAST_UPDATE = 2011-05-17 13:51:59.0

Writing parameter information for pipeline:

The result of this execution will be an exit code of 0 and no output

Pipeline_Configuration \ 
	-configuration ~/HiRISE/Configuration/EDRgen/EDRgen.conf \ 
      -pipeline HiColorNorm \ 
	  -parameter Make_Unfiltered_Cube \ 
	  -write \ 
      -value FALSE \ 
	  -validation "TRUE|FALSE|true|false"

Note that for write operations the -write option must be specified.

The result will be no output; the application will produce an exit status value of zero if there were no errors. @author Chris Van Horne - UA/HiROC @author Rodney Heyd - UA/HiROC @version 1.6 */ public class Pipeline_Configuration { /* Programmer notes: TODO(cwvh): The Pipeline_Configuration class currently has System.exit calls within its methods. This can be quite a headache for client code of this API since execution may abruptly terminate in the library. This should be fixed some day. TODO(rsh): Pipeline_Configuration needs to be tested against PostgreSQL. No testing of this utility against PostgreSQL has been done yet. */ /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Pipeline_Configuration.Pipeline_Configuration (1.6 2012/04/16 07:01:53)"; /** The default table name. */ public static final String DEFAULT_DATABASE_TABLE_NAME = "Pipeline_Configuration"; /** The default configuration filename. */ private static final String DEFAULT_CONFIGURATION = "Database.conf"; /** A Validatorcontains a list of commonly used validators for typical values. These validators may be used for convenience when doing a write. Java Strings used as regexps must still follow proper escaping using double-backslashes. Example: hiColorNormPipeConf.write ("Make_Unfiltered_Cube", "TRUE", Validator.BOOLEAN); */ public enum Validator { FLOATING_POINT ("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"), BOOLEAN ("TRUE|FALSE"); private final String regexp; private Validator ( String regexp ) {this.regexp = regexp;} public String regexp () {return regexp;} } /** Application exit status values. */ public enum ExitCode { SUCCESS (0), COMMAND_LINE_PARSE_ERROR (1), CONFIGURATION_ERROR (2), DATABASE_ERROR (3), STRICT_VALIDATION_FAILURE (4); private final int code; private ExitCode ( int code ) {this.code = code;} public int code () {return code;} } /** The fields of a record in the Pipeline_Configuration database table. When queries are made via read and write methods, a Conf is used as a data transfer object. */ public static final class Conf { public final String pipeline, parameter, value, validation, description, lastUpdate; public Conf () {this (null, null, null, null, null, null);} public Conf ( String pipeline, String parameter, String value, String validation, String description, String lastUpdate ) { this.pipeline = pipeline; this.parameter = parameter; this.value = value; this.validation = validation; this.description = description; this.lastUpdate = lastUpdate; } } private final Database database; private final Connection conn; private final String catalog; private final String table; private final String pipeline; private final boolean strict; private static String NL = Configuration.NL; /** Constructs a Pipeline_Configuration with non-strict semantics. @param database The Database object providing access to the database contents. @param catalog The name of catalog in the database where the database tables are located. @param table The name of the table to use for pipeline configuration queries. @param pipeline The name of the pipeline to query for parameters. */ public Pipeline_Configuration ( Database database, String catalog, String table, String pipeline ) {this (database, catalog, table, pipeline, false);} /** Constructs a Pipeline_Configuration. @param database The Database object providing access to the database contents. @param catalog The name of catalog in the database where the database tables are located. @param table The name of the table to use for pipeline configuration queries. @param pipeline The name of the pipeline to query for parameters. @param strict A flag that indicates whether to treat validation failures as global errors. */ public Pipeline_Configuration ( Database database, String catalog, String table, String pipeline, boolean strict ) { this.database = database; this.conn = database.Connection (); this.catalog = catalog; this.table = table; this.pipeline = pipeline; this.strict = strict; } /** Read the most up-to-date row for a parameter. @param parameter The name of a parameter in the table for which to read the record. @return A {@link Conf} containing all the field values of the record. */ public Conf read ( String parameter ) { String table = catalog + "." + this.table, fields = "value, validation_string, description, last_update", sql = "SELECT %s FROM %s WHERE pipeline = " + "? AND parameter = ? ORDER BY last_update DESC", query = String.format (sql, fields, table); try { PreparedStatement stmt = conn.prepareStatement (query); stmt.setString (1, pipeline); stmt.setString (2, parameter); ResultSet results = stmt.executeQuery(); if (results.next ()) { String value = results.getString ("value"), validation = results.getString ("validation_string"), /* The (?i:X) format allows case-insensitive searching in String.match. */ regexp = String.format ("(?i:%s)", validation); /* If strict mode is enabled, values must pass validation otherwise the program is exited with a failed exit code. */ if (strict && ! value.matches (regexp)) { String msg = "error: parameter '%s' did not pass value validation!\n"; System.err.printf (msg, parameter); System.exit (ExitCode.STRICT_VALIDATION_FAILURE.code ()); } String descr = results.getString ("description"), timestamp = results.getString ("last_update"); return new Conf (pipeline, parameter, value, validation, descr, timestamp); } } catch (SQLException exception) { String msg = "error: prepared statement for parameter '%s' failed with '%s'!\n"; System.err.printf (msg, parameter, exception); System.exit (ExitCode.DATABASE_ERROR.code()); } return new Conf (); } /** Write a simple parameter-value pair into a table record. @param parameter The parameter name. @param value The value String for the parameter. */ public void write ( String parameter, String value ) {write (parameter, value, null);} /** Write a parameter-value pair with validation string into a table record. @param parameter The parameter name. @param value The value String for the parameter. @param validation The validation String for the value. */ public void write ( String parameter, String value, String validation ) {write (parameter, value, validation, null);} /** Write a parameter-value pair with validation string and description into a table record. @param parameter The parameter name. @param value The value String for the parameter. @param validation The validation String for the value. @param description The description String for the parameter. */ public void write ( String parameter, String value, String validation, String description ) { Conf conf = new Conf (pipeline, parameter, value, validation, description, null); write (conf); } /** Write a configuration (Conf) for a parameter into a table record. The string-only variants of the write methods are preferable; they use this method. @param conf A {@link Conf} object which contains the field values of the record to be inserted into the table */ public void write ( Conf conf ) { String table = catalog + "." + this.table, fields = "pipeline, parameter, value, validation_string, description", query = String.format ("SELECT %s FROM %s WHERE pipeline = " + "? AND parameter = ? ORDER BY last_update ASC", fields, table), insert = String.format ("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?)", table, fields), update = String.format ("UPDATE %s SET value = ?, validation_string = " + "?, description = ? WHERE pipeline = ? AND parameter = ?", table); try { PreparedStatement stmt; ResultSet results; stmt = conn.prepareStatement (query); stmt.setString (1, conf.pipeline); stmt.setString (2, conf.parameter); results = stmt.executeQuery (); if (results.next ()) { /* The SELECT returned rows; do an UPDATE. Get the values in the row. */ String value = conf.value; if (value == null) value = results.getString ("value"); String validation = conf.validation; if (validation == null) validation = results.getString ("validation_string"); String description = conf.description; if (description == null) description = results.getString ("description"); stmt = conn.prepareStatement (update); stmt.setString (1, value); stmt.setString (2, validation); stmt.setString (3, description); stmt.setString (4, conf.pipeline); stmt.setString (5, conf.parameter); } else { /* The query didn't return a row; do an INSERT. Convert null conf values to empty Strings. */ String value = conf.value; if (value == null) value = ""; String validation = conf.validation; if (validation == null) validation = ""; String description = conf.description; if (description == null) description = ""; stmt = conn.prepareStatement (insert); stmt.setString (1, conf.pipeline); stmt.setString (2, conf.parameter); stmt.setString (3, value); stmt.setString (4, validation); stmt.setString (5, description); } stmt.execute (); } catch (SQLException exception) { String msg = "error: prepared statement for write failed with '%s'\n"; System.err.printf (msg, exception); System.exit (ExitCode.DATABASE_ERROR.code()); } } /** Close the database connection. Closing the database connection is usually not necessary. Use this method to ensure that the database is closed when needed. WARNING Do not attempt to read or write a database table record after the connection to the database has been closed. */ public void close () { try {conn.close ();} catch (SQLException exception) {exception.printStackTrace ();} } /** Build a configuration table given a database catalog and table name. A database table for holding configuration parameters is created in the specified catalog. @param catalog The name of the database catalog to contain the table. @param table The name of the database table for containing configuration parameters. @throws Database_Exception if the database table could not be created. */ public void buildConfigurationTable ( String catalog, String table ) throws Database_Exception { Vector field_names = new Vector (); field_names.add ("ID"); field_names.add ("PIPELINE"); field_names.add ("PARAMETER"); field_names.add ("VALUE"); field_names.add ("VALIDATION_STRING"); field_names.add ("DESCRIPTION"); field_names.add ("LAST_UPDATE"); Vector field_types = new Vector (); field_types.add ("INT(4)"); field_types.add ("VARCHAR(75)"); field_types.add ("VARCHAR(75)"); field_types.add ("TEXT"); field_types.add ("TEXT"); field_types.add ("TEXT"); field_types.add ("TIMESTAMP"); try {database.Create (catalog + "." + table, field_names, field_types);} catch (Database_Exception exception) { throw new Database_Exception (ID + NL + "Could not create the \"" + table + "\" in the \"" + catalog + "\" catalog." + NL + exception.getMessage ()); } } /** Application main. @param args The program command-line arguments. */ public static void main ( String[] args ) { try { CommandLineParser parser = new GnuParser (); Options options = buildCommandLine (); CommandLine line = parser.parse (options, args); String config = null; if (args.length == 0 || line.hasOption ("help")) { usage (options); System.exit (ExitCode.SUCCESS.code ()); } /* Get the configuration while respecting $HiRISE_ROOT. TODO(cwvh): Might want to allow overriding $HiRISE_ROOT from the command-line; e.g. String root = System.getenv ("HiRISE_ROOT"); */ if (line.hasOption ("configuration")) config = line.getOptionValue ("configuration"); else config += DEFAULT_CONFIGURATION; // Get the catalog from the configuration. Configuration configuration = new Configuration (config); String catalog = configuration.Get_One ("CATALOG"); if (line.hasOption ("catalog")) { catalog = line.getOptionValue ("catalog"); configuration.Set ("CATALOG", catalog); } String table = DEFAULT_DATABASE_TABLE_NAME; if (line.hasOption ("table")) table = line.getOptionValue ("table"); if (line.hasOption ("buildtable")) { Database db = new Database (configuration); db.Connect (); Pipeline_Configuration pipeline_configuration = new Pipeline_Configuration (db, catalog, table, "all", true); pipeline_configuration.buildConfigurationTable (catalog, table); System.exit (ExitCode.SUCCESS.code ()); } // Get the pipeline for the query. if (! line.hasOption ("pipeline")) { System.err.println ("error: need a 'pipeline' for this query"); System.exit (ExitCode.COMMAND_LINE_PARSE_ERROR.code ()); } String pipeline = line.getOptionValue ("pipeline"); // Get the parameter for this pipeline. if (! line.hasOption ("parameter")) { usage (options); System.err.println ("error: need a 'parameter' argument to query"); System.exit (ExitCode.COMMAND_LINE_PARSE_ERROR.code ()); } String parameter = line.getOptionValue ("parameter"); // Enable strict-mode? boolean strict = line.hasOption ("strict"); // Construct the database and pipeline configuration. Database database = new Database (configuration); database.Connect (); Pipeline_Configuration pipeline_configuration = new Pipeline_Configuration (database, catalog, table, pipeline, strict); if (line.hasOption ("write")) { /* Write to the table. Get the value to write. */ if (! line.hasOption ("value")) { System.err.println ("error: need a 'value' to write in write-mode"); System.exit (ExitCode.COMMAND_LINE_PARSE_ERROR.code()); } String value = line.getOptionValue ("value"); // Optional validation string. String validation = null; if (line.hasOption ("validation")) validation = line.getOptionValue("validation"); // Optional description. String description = null; if (line.hasOption ("description")) description = line.getOptionValue ("description"); pipeline_configuration.write (parameter, value, validation, description); } else { // Read from the table. Conf conf = pipeline_configuration.read (parameter); String out; if (line.hasOption ("info")) { StringBuilder fmt = new StringBuilder(); fmt .append ("PIPELINE = %s").append ("\n") .append ("PARAMETER = %s").append ("\n") .append ("VALUE = %s").append ("\n") .append ("VALIDATION_STRING = %s").append ("\n") .append ("DESCRIPTION = %s").append ("\n") .append ("LAST_UPDATE = %s"); out = String.format (fmt.toString (), conf.pipeline, conf.parameter, conf.value, conf.validation, conf.description, conf.lastUpdate); } else out = String.format ("%s", conf.value); System.out.println (out); } } catch (ParseException exception) { System.err.println (exception.getMessage ()); System.exit (ExitCode.COMMAND_LINE_PARSE_ERROR.code ()); } catch (Configuration_Exception exception) { System.err.println (exception.getMessage ()); System.exit (ExitCode.CONFIGURATION_ERROR.code ()); } catch (Database_Exception exception) { System.err.println (exception.getMessage ()); System.exit (ExitCode.DATABASE_ERROR.code ()); } } /** Print application usage. */ private static void usage ( Options options ) { HelpFormatter formatter = new HelpFormatter (); formatter.printHelp ("Pipeline_Configuration", options); } // Build the command-line parameters for further parsing. @SuppressWarnings ("static-access") private static Options buildCommandLine () { Option help = new Option ("help", "print this message"), strict = new Option ("strict", "treat value validation failures as global errors"), info = new Option ("info", "show all available information for a parameter"), write = new Option ("write", "enable write-mode for updating or adding a pipeline configuration"); Option buildtable = OptionBuilder .withArgName ("buildtable") .hasArg (false) .withDescription ("Create a new configuration table.") .create ("buildtable"); Option catalog = OptionBuilder .withArgName ("catalog") .hasArg () .withDescription ("override the configuration catalog (useful for testing)") .create ("catalog"); Option configuration = OptionBuilder .withArgName ("configuration") .hasArg () .withDescription ("configuration file") .create ("configuration"); Option parameter = OptionBuilder .withArgName ("parameter") .hasArg () .withDescription ("the parameter to query") .create ("parameter"); Option pipeline = OptionBuilder .withArgName ("pipeline") .hasArg () .withDescription ("the pipeline to use when querying for a parameter") .create ("pipeline"); Option table = OptionBuilder .withArgName ("table") .hasArg () .withDescription ("the table containing the configuration " + "parameters, default: " + DEFAULT_DATABASE_TABLE_NAME) .create("table"); Option value = OptionBuilder .withArgName ("value") .hasArg () .withDescription ("write-mode: the value to write") .create ("value"); Option validation = OptionBuilder .withArgName ("validation") .hasArg () .withDescription ("write-mode: the validation string to write") .create ("validation"); Option description = OptionBuilder .withArgName ("description") .hasArg () .withDescription ("write-mode: the description to write") .create ("description"); Option quiet = OptionBuilder .withArgName("quiet") .hasArg(false) .withDescription("suppress the output of command and version information") .create("quiet"); Options options = new Options (); options.addOption (help); options.addOption(quiet); options.addOption (catalog); options.addOption (configuration); options.addOption (strict); options.addOption (write); options.addOption (parameter); options.addOption (pipeline); options.addOption (table); options.addOption (info); options.addOption (value); options.addOption (validation); options.addOption (description); options.addOption (buildtable); return options; } } pirl-2.3.8/PIRL/Conductor/Pipeline_Configuration/package.html0000644000175000017500000000352011742741741023777 0ustar mathieumathieu The PIRL Conductor Pipeline_Configuration package provides an application to manage or retrieve pipeline configuration parameters that are stored in a database.

Pipeline procedures often require external configuration parameters that are usually stored within a configuration file. However, this often places management burden on the users that monitor and maintain running pipelines. Different processing "modes" may require different configuration settings, which often requires different configuration files or sets of configuration files if multiple pipelines are involved. The utility in this package allows these configuration parameters to be stored in and retrieved from a database table, and can include a regex field to validate/restrict the parameter values stored for each parameter name. @see PIRL.Database pirl-2.3.8/PIRL/Conductor/Unresolved_Reference.java0000644000175000017500000000460511742733133022032 0ustar mathieumathieu/* Unresolved_Reference PIRL CVS ID: Unresolved_Reference.java,v 1.6 2012/04/16 06:04:11 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; /** The Exeception that is thrown if a parameter source reference or database field reference can not be resolved and a Default_Value has not been provided.

@author Bradford Castalia - UA/PIRL @version 1.6 @see Reference_Resolver */ public class Unresolved_Reference extends Exception { /** Class name and version identification. */ public static final String ID = "PIRL.Conductor.Unresolved_Reference (1.6 2012/04/16 06:04:11)"; private String Reference = null; /** Constructs an Unresolved_Reference exception with the specified detail message.

The exception's Reference will be null.

@param message The detail message; retrievable by the {@link Throwable#getMessage()} method. */ public Unresolved_Reference ( String message ) {super (message);} /** Constructs an Unresolved_Reference exception with the specified detail message and reference.

@param message The detail message; retrievable by the {@link Throwable#getMessage()} method. @param reference The reference String that could not be resolved; retrievable by the {@link #Reference()} method. */ public Unresolved_Reference ( String message, String reference ) { super (message); Reference = reference; } /** Gets the unresolved reference.

@return The unresolved reference String. */ public String Reference () {return Reference;} } pirl-2.3.8/PIRL/Conductor/Management.java0000644000175000017500000003647111742733132020007 0ustar mathieumathieu/* Management PIRL CVS ID: Management.java,v 1.17 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Database.Database_Exception; import PIRL.Messenger.Message; import java.io.Writer; import java.io.IOException; import java.util.Vector; /** Management is the Conductor management interface.

The Conductor Management interface provides the ability to set and get various processing state and control information from a Conductor. It also provides registration control for processing state change event notifications and log stream writer attachment.

@author Bradford Castalia - UA/PIRL @version 1.17 @see Conductor */ public interface Management { /** Get the Conductor {@link Conductor#Identity() identity}.

@return A Message containing the Conductor identification parameters. @see Conductor#Identity() */ public Message Identity (); /** Get the Conductor Configuration.

The Configuration that is returned is a copy of the current state of the Conductor Configuration, which is dynamically changed during processing. All parameters named "PASSWORD" (case insensitive) will have their values reset to {@link Processing_Changes#MASKED_PASSWORD}.

@return A copy of the Conductor Configuration. @see Conductor#Preconfigure(Configuration) @see Conductor#Postconfigure(Configuration) */ public Configuration Configuration (); /** Start Conductor source record processing.

If the Conductor is in a negative {@link #Processing_State() processing state} ({@link Conductor#WAITING waiting } or {@link Conductor#HALTED halted}) it will enter the {@link Conductor#RUNNING running} state, reconfigure itself from its Configuration source, and source record processing will commence. If the Conductor is in the {@link Conductor#RUN_TO_WAIT run-to-wait} state it will clear this to the running state; for any other positive state (running or {@link Conductor#POLLING polling}) there will be no change.

@see Conductor#Start() */ public void Start (); /** Request that Conductor stop source record processing.

If the Conductor is in a positive {@link #Processing_State() processing state} it will enter the {@link Conductor#RUN_TO_WAIT run-to-wait} state, in which it will enter the {@link Conductor#WAITING waiting} state when the current source record completes processing. If the Conductor is in the {@link Conductor#POLLING polling} state it will immediately stop polling for new source records. There will be no effect for any negative state.

@see Conductor#Stop() */ public void Stop (); /** Immediately stop Conductor processing and exit.

Any open log file is closed. The database server is disconnected. An {@link Processing_Changes#Exiting(boolean) exiting} processing event is sent to all {@link #Add_Processing_Listener(Processing_Listener) processing listeners}. The application exits with a {@link Conductor#EXIT_SUCCESS success status}.

N.B.: If source processing is {@link #Processing_State() running} it is aborted.

@see Conductor#Quit() */ public void Quit (); /** Get the current Conductor processing state.

The possible processing state values are:

{@link Conductor#RUNNING}
Source records are being processing.
{@link Conductor#POLLING}
No unprocessed source records are currently available for processing; the Conductor is {@link #Poll_Interval(int) polling} for source records to process.
{@link Conductor#RUN_TO_WAIT}
When processing of the current source record completes Conductor will go into the waiting state.
{@link Conductor#WAITING}
The Conductor is waiting to be told to being processing.
{@link Conductor#HALTED}
A problem condition caused the Conductor to halt processing. The problem may be the result of the maximum number of {@link #Stop_on_Failure(int) sequential failures} of source record processing having occured, a database access failure, or some other {@link #Processing_Exception system error}.

The WAITING and HALTED state codes are negative; all others are positive.

@return A Conductor processing state code. */ public int Processing_State (); /** Get the current cache of source records.

The table is expected to be delivered with the record fields in {@link Conductor#SOURCES_FIELD_NAMES} order, and with the records sorted by increasing {@link Conductor#SEQUENCE_FIELD} order.

N.B.: Only the contents of the Conductor source records cache is delivered. The contents of the database sources table may be much, much larger.

@return A table Vector of record Vectors of field Strings. The first record contains the field names. */ public Vector> Sources (); /** Get the procedures table.

The table is expected to be delivered with the record fields in {@link Conductor#PROCEDURES_FIELD_NAMES} order, and with the records in processing order.

N.B.: The entire contents of the database procedures table is delivered.

@return A table Vector of record Vectors of field Strings. The first record contains the field names. */ public Vector> Procedures (); /** Get the current Conductor processing state variables.

All Processing_Changes values are set except the except the flag variables - {@link Processing_Changes#Sources_Refreshed(boolean)}, {@link Processing_Changes#Procedures_Changed(boolean)} and {@link Processing_Changes#Exiting(boolean)} - which will always be false.

@return Processing_Changes object. @see Processing_Changes */ public Processing_Changes State (); /** Register a processing state change listener.

The Conductor sends its {@link Processing_Event processing event} notifications to all registered listeners.

@param listener A Processing_Listener. @return This Management object. @see Processing_Listener */ public Management Add_Processing_Listener ( Processing_Listener listener ); /** Unregister a processing state change listener.

@param listener The Processing_Listener to be removed from the Management list of registered listeners. @return true If the listener was registered and is now removed; false if it was not registered. @see #Add_Processing_Listener(Processing_Listener) */ public boolean Remove_Processing_Listener ( Processing_Listener listener ); /** Register a Writer to receive processing log stream output.

The Conductor writes its processing log reports, including the output from all pipeline procedures it runs, to all registered log Writers.

@param writer A Writer object. @return This Management object. @see #Enable_Log_Writer(Writer, boolean) @see #Remove_Log_Writer(Writer) */ public Management Add_Log_Writer ( Writer writer ); /** Unregister a log Writer.

@param writer A Writer object. @return true If the writer was registered and is now removed; false if it was not registered. @see #Add_Log_Writer(Writer) */ public boolean Remove_Log_Writer ( Writer writer ); /** Enable or disable output to a {@link #Add_Log_Writer(Writer) registered log stream Writer}.

@param writer A Writer that has been registered to receive Conductor log stream output. If the writer is not {@link #Add_Log_Writer(Writer) registered} to receive the Conductor log stream nothing is done. @param enable If false, Conductor log stream output to the Writer is suspended without having to unregister the Writer. If true, a Writer that has had its log stream output suspended will begin receiving it again. @return This Management object. */ public Management Enable_Log_Writer ( Writer writer, boolean enable ); /** Set the interval at which the Conductor will poll for unprocessed source records.

When the Conductor is in the polling {@link #Processing_State() state} it will periodically check the database sources table for source records that are marked as unprocessed. Unprocessed source records are used to refresh the Conductor cache of source records to be processed. When all source records in the cache have been processed, the Conductor will enter the polling state if the poll interval is positive; it will enter the waiting state if the poll interval is not positive (as if the {@link #Stop() stop request} had been issued), or will exit if it is not {@link #Connected_to_Stage_Manager() connected to a Stage_Manager} at the time.

@param seconds The interval, in seconds, at which the Conductor will poll for unprocessed source records. If less than zero, zero will be set, but the value will be reset to the Conductor Configuration {@link Conductor#POLL_INTERVAL_PARAMETER} value or the {@link Conductor#DEFAULT_POLL_INTERVAL} value if the Configuration does contain the parameter, the next time the Conductor is {@link #Start() started} after having stopped processing for any reason. @return This Management object. @see Conductor#Poll_Interval(int) */ public Management Poll_Interval ( int seconds ); /** Get the interval at which the Conductor will poll for unprocessed source records.

This value may also be obtained from the {@link #Configuration() Configuration} {@link Conductor#POLL_INTERVAL_PARAMETER}.

@return The interval, in seconds, at which the Conductor will poll for unprocessed source records. If zero, polling has been disabled. @see #Poll_Interval(int) */ public int Poll_Interval (); /** Set the Conductor default Reference_Resolver value.

Normally when the Conductor Reference_Resolver is unable to resolve a reference it will throw an {@link #Processing_Exception() exception} and enter the halted {@link #Processing_State() processing state}, or exit if it is not {@link #Connected_to_Stage_Manager() connected to a Stage_Manager} at the time. If, however, the {@link Reference_Resolver#Default_Value(String) Reference_Resolver default value} is set to a non-null String that value will be used for the unresolved_reference instead of throwing an exception.

@param value The default Reference_Resolver String value. If this starts with {@link Conductor#UNRESOLVED_REFERENCE_THROWS} (case insensitive) null will be used. @return This Management object. @see Conductor#Resolver_Default_Value(String) @see Reference_Resolver */ public Management Resolver_Default_Value ( String value ); /** Get the Conductor default Reference_Resolver value.

This value may also be obtained from the {@link #Configuration() Configuration} {@link Conductor#UNRESOLVED_REFERENCE_PARAMETER}. Note, however, that the value will be {@link Conductor#UNRESOLVED_REFERENCE_THROWS} when the actual value is null.

@return The default Reference_Resolver String value. @see #Resolver_Default_Value(String) */ public String Resolver_Default_Value (); /** Set the number of Conductor sequential source processing failures at which to stop processing.

The Conductor keeps a {@link #Sequential_Failures() count of sequential source processing failures}. When this count reaches the stop-on-failure limit the Conductor will enter the halted {@link #Processing_State() processing state} or exit if it is not {@link #Connected_to_Stage_Manager() connected to a Stage_Manager} at the time.

N.B.: Reaching the stop-on-failure limit does not cause the sequential failures count to be {@link #Reset_Sequential_Failures() reset}.

@param failure_count The number of Conductor sequential source processing failures at which to stop processing. If zero sequential processing failures will never cause processing to stop. If negative the current value will not be changed, but the value will be reset to the Conductor Configuration {@link Conductor#STOP_ON_FAILURE_PARAMETER} value or the {@link Conductor#DEFAULT_STOP_ON_FAILURE} value if the Configuration does contain the parameter, the next time the Conductor is {@link #Start() started} after having stopped processing for any reason. @return This Management object. @see #Sequential_Failures() @see Conductor#Stop_on_Failure(int) */ public Management Stop_on_Failure ( int failure_count ); /** Get the number of Conductor sequential source processing failures at which to stop processing.

This value may also be obtained from the {@link #Configuration() Configuration} {@link Conductor#STOP_ON_FAILURE_PARAMETER}

@return The number of Conductor sequential source processing failures at which to stop processing. If zero sequential processing failures will never cause processing to stop. @see #Stop_on_Failure(int) */ public int Stop_on_Failure (); /** Get the count of sequential source processing failures that the Conductor has accumulated.

Other source processing success and failure counts may be obtained from the {@link #Configuration() Configuration}:

{@link Conductor#SOURCE_SUCCESS_COUNT}
The total number of source records that have successfully completed all processing since the Conductor first began processing.
{@link Conductor#SOURCE_FAILURE_COUNT}
The number of source records that have resulted in a failed procedure condition during processing.
{@link Conductor#TOTAL_FAILURE_COUNT}
The total number of source records that failed to be processed for any reason since the Conductor first began processing.

@return The count of sequential source processing failures that the Conductor has accumulated. @see #Stop_on_Failure(int) @see #Reset_Sequential_Failures() */ public int Sequential_Failures (); /** Reset the count of sequential source processing failures that the Conductor has accumulated.

If the Conductor is in the halted {@link #Processing_State() processing state} it is reset to the waiting state.

@return This Management object. @see #Stop_on_Failure(int) @see Conductor#Reset_Sequential_Failures() */ public Management Reset_Sequential_Failures (); /** Get the exception that caused Conductor processing to halt.

N.B.: When Conductor processing is {@link #Start() started} the previous processing exception is cleared.

@return The Exception that caused processing to halt. This will be null if processing did not halt as the result of an exception, or the current processing state is not halted. */ public Exception Processing_Exception (); /** Test if the Conductor is connected to a Stage_Manager.

@return true if the Conductor is connected to a Stage_Manager via an open Local_Theater; false otherwise. @see PIRL.Conductor.Maestro.Local_Theater */ public boolean Connected_to_Stage_Manager (); } pirl-2.3.8/PIRL/Conductor/Makefile0000644000175000017500000000554711710106110016511 0ustar mathieumathieu# Makefile for Java Conductor package. # # PIRL CVS ID: Makefile,v 1.26 2012/01/25 23:12:08 castalia Exp # gmake syntax # Installation location for the executable scripts. SCRIPTS_DIR ?= /opt/local/sh # Installation location for the JNI library. JNI_LIB_DIR ?= /opt/local/lib # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm # Location of SwingX Classes. SwingX ?= /opt/java/SwingX/swingx.jar # Location of the JavaMail Classes (used by Notify). JavaMail ?= /opt/java/javamail/mail.jar JPATH ?= ../..:$(JCM):$(SwingX):$(JavaMail) JC ?= javac CLASSES := Reference_Resolver.class \ Unresolved_Reference.class \ String_Vector_Comparator.class \ Stream_Logger.class \ Management.class \ Processing_Changes.class \ Processing_Event.class \ Processing_Listener.class \ Colors.class \ Conductor.class \ Table_Mouse_Listener.class \ Sources_Table_Model.class \ Sources_Table.class \ Procedures_Table_Model.class \ Procedures_Table.class \ Manager.class \ Notify.class \ Evaluate.class \ Pipeline_Source_Manager.class SCRIPTS := Conductor \ Conductor1 \ Create_Pipeline \ Notify \ Pipeline_Source \ Pipeline_Source_Reset \ #=============================================================================== # Native methods library LIBRARY := Native_Methods CLASSES += $(LIBRARY).class # Host machine architecture identification. empty := space := $(empty) $(empty) OS := $(subst $(space),_,$(shell uname -s)) ARCH := $(subst $(space),_,$(shell uname -m)) # Java installation location. JNI_ROOT ?= /usr/java # Compiler. CC = gcc # Preprocessor flags. CPPFLAGS += -I$(JNI_ROOT)/include # Platform specifics ifeq ($(OS),Darwin) EXT := jnilib CFLAGS += -bundle else EXT := so CFLAGS += -fPIC -shared endif JNI_DIR := $(OS).$(ARCH) JNI_LIBRARY := $(JNI_DIR)/lib$(LIBRARY).$(EXT) # Targets: all: classes jni Maestro Pipeline_Configuration classes: $(CLASSES) jni: $(JNI_LIBRARY) $(JNI_LIBRARY): $(JNI_DIR) $(LIBRARY).c $(CC) $(CPPFLAGS) $(CFLAGS) $(LIBRARY).c -o $@ $(JNI_DIR): mkdir $(JNI_DIR) Maestro: @echo @echo "--> Conductor/$@" @$(MAKE) --no-print-directory -C $@ Pipeline_Configuration: @echo @echo "--> Conductor/$@" @$(MAKE) --no-print-directory -C $@ install: all cp -p $(SCRIPTS) $(SCRIPTS_DIR) cp -p $(JNI_LIBRARY) $(JNI_LIB_DIR) $(MAKE) -C Maestro install $(MAKE) -C Pipeline_Configuration install patch: gtar -c -z -f Process.patch.tar.gz \ --exclude=CVS \ --exclude=Install \ --exclude=README-Process.patch \ Process.patch clean: rm -f *.class $(JNI_LIBRARY) $(MAKE) -C Maestro clean $(MAKE) -C Pipeline_Configuration clean .PHONY: all classes Maestro Pipeline_Configuration jni install patch clean .SUFFIXES: .java .class .java.class: $(JC) -classpath $(JPATH) $(JFLAGS) $< pirl-2.3.8/PIRL/Conductor/Processing_Listener.java0000644000175000017500000000365711742733132021714 0ustar mathieumathieu/* Processing_Listener PIRL CVS ID: Processing_Listener.java,v 1.5 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.util.EventListener; /** A Processing_Listener specifies the interface to receive Conductor Processing_Event notifications.

Implementors of the Processing_Listener interface can {@link Management#Add_Processing_Listener(Processing_Listener) register} with the Conductor Management interface to receive Conductor Processing_Event notifications

@author Bradford Castalia - UA/PIRL @version 1.5 @see Management */ public interface Processing_Listener extends EventListener { /** A Conductor Processing_Event occured.

Whenever a significant processing event occurs by a Conductor it sends a Processing_Event to all {@link Management#Add_Processing_Listener(Processing_Listener) registered} listeners.

@param event A Conductor Processing_Event. @see Processing_Event */ public void Processing_Event_Occurred ( Processing_Event event ); } pirl-2.3.8/PIRL/Conductor/String_Vector_Comparator.java0000644000175000017500000001262411742733133022705 0ustar mathieumathieu/* String_Vector_Comparator PIRL CVS ID: String_Vector_Comparator.java,v 1.9 2012/04/16 06:04:11 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import java.util.Comparator; import java.util.List; /** A String_Vector_Comparator implements the Comparator interface to sort Lists of Objects.

It is particularly useful, for example, in sorting a set of results from a Database query. Use an instance of this class as the Comparator in the Collections.sort(List, Comparator) static method, which actually does the sorting. The Objects to be compared are assumed to be Lists. The Objects at the specified index of each List are converted to Strings. If these Strings can be parsed as Doubles then these are compared numerically. Otherwise the Strings are are compared lexicographically.

@author Christian Schaller, Bradford Castalia - UA/PIRL */ public class String_Vector_Comparator implements Comparator { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.String_Vector_Comparator (1.9 2012/04/16 06:04:11)"; // Vector index to be compared. private int Index; private static final String NL = System.getProperty ("line.separator"); /*============================================================================== Constructors */ /** Construct a String_Vector_Comparator using a sepecified index for the Lists to be {@link #compare(Object, Object) compared}.

@param index The List index to be compared. */ public String_Vector_Comparator ( int index ) {Index = index;} /** Construct a String_Vector_Comparator that will {@link #compare(Object, Object) compare} index zero of the Lists. */ public String_Vector_Comparator () {this (0);} /*============================================================================== Accessors */ /** Set the List index to be {@link #compare(Object, Object) compared}.

@param index The List index to be compared. @return This String_Vector_Comparator. @throws IllegalArgumentException If the index is negative. */ public String_Vector_Comparator Index ( int index ) { if (index < 0) throw new IllegalArgumentException (ID + NL + "A negative (" + index + ") index is invalid."); Index = index; return this; } /** Get the List index to be {@link #compare(Object, Object) compared}.

@return The List index to be compared. @see #Index(int) */ public int Index () {return Index;} /*============================================================================== Methods */ /** Compare two Lists.

The objects at each List's {@link #Index() Index} entry are first checked for being null. If both are null, 0 is returned; if only the first is null, -1 is returned; if only the second is null, 1 is returned.

Non-null list entries are converted to a Strings. If these Strings can be converted to Doubles, then they are compared numerically using Double.compareTo. Otherwise they are compared lexicographically using String compareTo.

@param list_0 The first List for comparison. @param list_1 The second List for comparison. @return An integer less than, equal to, or greater than zero as the first List {@link #Index() Index} entry is less than, equal to, or greater than the second List {@link #Index() Index} entry. @throws IllegalArgumentException If the arguments are not Lists. @throws ArrayIndexOutOfBoundsException If the arguments do not have at least {@link #Index() Index} + 1 entries. */ public int compare ( Object list_0, Object list_1 ) { if (! (list_0 instanceof List) || ! (list_1 instanceof List)) throw new IllegalArgumentException (ID + NL + "The objects to compare must be Lists."); if (((List)list_0).size () <= Index || ((List)list_1).size () <= Index) throw new ArrayIndexOutOfBoundsException (ID + NL + "Both List objects to compare must contain at least " + (Index + 1) + " entr" + (((Index + 1) == 1) ? "y" : "ies") + '.'); Object object_0 = ((List)list_0).get (Index), object_1 = ((List)list_1).get (Index); if (object_0 == null || object_1 == null) { if (object_0 == null && object_1 == null) return 0; if (object_0 == null) return -1; if (object_1 == null) return 1; } String string_0 = object_0.toString (), string_1 = object_1.toString (); try { // Try numerical comparison. Double double_0 = new Double (string_0), double_1 = new Double (string_1); return double_0.compareTo (double_1); } catch (NumberFormatException exception) { // Compare lexicographically. return string_0.compareTo (string_1); } } } pirl-2.3.8/PIRL/Conductor/Evaluate.java0000644000175000017500000000551511742733132017474 0ustar mathieumathieu/* Evaluate PIRL CVS ID: Evaluate.java,v 1.6 2012/04/16 06:04:10 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import edu.hws.jcm.data.*; /** Evaluates a mathematical expression.

This class is a wrapper for the mathematical expression evaluator. It is provided to confirm that expressions processed by a Reference_Resolver will correctly evaluate; and if not, why not.

The arguments on the command line are taken to be the expression. This may be contained in one argument or multiple arguments; multiple arguments are concatenated with spaces separating them to form a single expression.

The expression is evaluated and the results are listed. If the expression can not be evaulated the expression evaluator's description of the problem is listed.

@author Bradford Castalia @version 1.6 @see edu.hws.jcm.data.Parser#Parser() */ public class Evaluate { public static void main ( String[] args ) { String expression = ""; if (args.length == 0) { System.out.println ("Usage: Evaluate "); System.exit (1); } else { expression = args[0]; for (int count = 1; count < args.length; count++) expression += ' ' + args[count]; } Parser parser = new Parser (); double result; try { result = parser.parseLogical (expression).getVal (); System.out.println ("Logical expression = " + ((result == 0.0) ? "false" : " true")); } catch (ParseError exception) { try { result = parser.parse (expression).getVal (); System.out.println ("Numeric expression = " + result); } catch (ParseError parse_error) { System.out.println ("Unable to evaluate the expression -\n" +"\"" + parse_error.context.data + "\"\n" +"at index " + parse_error.context.pos + ((parse_error.context.tokenString == null) ? "" : (" for token \"" + parse_error.context.tokenString + '"')) + ":\n" + parse_error.getMessage ()); System.exit (-1); } } System.exit (0); } } pirl-2.3.8/PIRL/Conductor/Conductor0000755000175000017500000001523611710106110016733 0ustar mathieumathieu#!/usr/bin/perl # # Conductor # # A wrapper for the Conductor process management application. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath or provided as the value of an environment # variable having the names listed here: # # MySQL_JDBC - http://dev.mysql.com/downloads/connector/j/ # # The pathname to the MySQL JDBC driver jar file or the root directory # where its class files are located. Either the MySQL JDBC Driver or # the PostgreSQL JDBC Driver is required to provide Database support. # Default: $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # # PostgreSQL_JDBC - http://jdbc.postgresql.org/ # # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. Either the PostgreSQL # JDBC Driver or the MySQL JDBC Driver is required to provide Database # support. # Default: $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # SwingX - http://swinglabs.org/ # # The pathname to the SwingLabs Swing Component Extensions jar file or # the root directory where its class files are located. This package is # a required dependency. # Default: $PIRL_JAVA_HOME/SwingX/swingx.jar # # JAVAMAIL - http://www.oracle.com/technetwork/java/javamail/ # JAF - http://java.sun.com/products/archive/javabeans/jaf11.html # # The pathnames to the JavaX JavaMail and JavaBeans Activation # Framework jar files or the root directory where their class files # are located. These packages provide email message sending # functionality as used by the Notify class. Note: These packages are # only required when Notify must use an "unfriendly" email server. # They may not be required at all, in which case a Notify-simple.java # implementation is provided that only uses JFC classes and worked # fine for many years until a "sophisticated" email server was # encountered. # # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # Conductor's Native_Methods library is required to enable a Conductor # to obtain its process ID which is appended to the hostname so each # Conductor running on a system will be uniquely identified by the # Conductor_ID field value in the Sources table. Though this is not # required it is recommended. The Native_Methods library is built at # the same time that the Conductor class files are built; the library # can be found in the PIRL/Conductor/. directory, where # is the operating system name (from uname -s) and is # the host system architecture (from uname -m). The directory where # this library is located should be in the LD_LIBRARY_PATH # (DYLD_LIBRARY_PATH for Apple OS X systems) environment variable value # (a colon separated list of directory pathnames). An attempt will be # made to find the library in the $PIRL_JAVA_HOME and $CLASSPATH # pathname list. If it is found the pathname to the libarary is # appended to the LD_LIBRARY_PATH (or DYLD_LIBRARY_PATH) environment # variable value. # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the pathnames # searched for dynamically linked libraries such as Conducor's # Native_Methods library. # # # CVS ID: Conductor,v 2.5 2012/01/25 23:12:08 castalia Exp $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $MySQL_JDBC = "$PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar" unless $MySQL_JDBC = $ENV{"MySQL_JDBC"}; Add_to_CLASSPATH ($MySQL_JDBC); $PostgreSQL_JDBC = "$PIRL_JAVA_HOME/PostgreSQL/postgresql.jar" unless $PostgreSQL_JDBC = $ENV{"PostgreSQL_JDBC"}; Add_to_CLASSPATH ($PostgreSQL_JDBC); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); $SwingX = "$PIRL_JAVA_HOME/SwingX/swingx.jar" unless $SwingX = $ENV{"SwingX"}; Add_to_CLASSPATH ($SwingX); $JAVAMAIL = "$PIRL_JAVA_HOME/javamail/mail.jar" unless $JAVAMAIL = $ENV{"JAVAMAIL"}; Add_to_CLASSPATH ($JAVAMAIL); $JAF = "$PIRL_JAVA_HOME/jaf/activation.jar" unless $JAF = $ENV{"JAF"}; Add_to_CLASSPATH ($JAF); @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; # Apple's OS X JVM won't allow Java classes to be run remotely # if they employ any graphics (Swing) functionality # regardless of whether this functionally is actually used. # The workaround is to tell the JVM to run "headless" when # the monitor mode is not specified on the command line. $Graphics_Mode = "-Djava.awt.headless=true"; foreach $arg (@ARGV) { if ($arg =~ /^-[Mm]/) { undef $Graphics_Mode; last; } } push @Arguments, $Graphics_Mode if $Graphics_Mode; push @Arguments, "PIRL.Conductor.Conductor", @ARGV; # Search for the Conductor Native_Methods library. chomp ($OS = `uname -s`); if ($OS == "Darwin") {$ld_library_path_name = "DYLD_LIBRARY_PATH"} else {$ld_library_path_name = "LD_LIBRARY_PATH";} if ($CLASSPATH) { chomp ($ARCH = `uname -m`); $JNI_DIR = "$OS.$ARCH"; @paths = split /:/, $CLASSPATH; foreach $path (@paths) { if (-d "$path/PIRL/Conductor/$JNI_DIR") { $ld_libary_path = $ENV{$ld_library_path_name}; if ($ld_libary_path) { $ld_libary_path = "$path/PIRL/Conductor/$JNI_DIR:$ld_libary_path"; } else { $ld_libary_path = "$path/PIRL/Conductor/$JNI_DIR"; } $ENV{$ld_library_path_name} = $ld_libary_path; last; } } } exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/Manager.java0000644000175000017500000016563611742733132017313 0ustar mathieumathieu/* Manager PIRL CVS ID: Manager.java,v 1.55 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import PIRL.Conductor.Maestro.Remote_Management_Exception; import PIRL.Conductor.Maestro.Error_Report; // PIRL packages import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.PVL.Parameter; import PIRL.Viewers.Splash_Screen; import PIRL.Viewers.Stream_Monitor; import PIRL.Viewers.Dialog_Box; import PIRL.Viewers.Icons; import PIRL.Viewers.Parameter_View; import PIRL.Viewers.View_Locator; import PIRL.Viewers.Blinker; import PIRL.Viewers.Percent_Bar; import javax.swing.JRootPane; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JCheckBoxMenuItem; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.Box; import javax.swing.BorderFactory; import javax.swing.JScrollPane; import javax.swing.JOptionPane; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.ImageIcon; import javax.swing.KeyStroke; import javax.swing.ToolTipManager; import javax.swing.UIManager; import javax.swing.SwingUtilities; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.border.Border; import java.awt.Toolkit; import java.awt.Color; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.Vector; import java.util.Iterator; /** A Manager is a GUI for managing a Conductor.

Configuration parameters

The following configuration parameters may be used by a Conductor Manager:

Splash_Screen
While the Manager is initializing the GUI it will, by default, display a spash screen. To disable this feature set the Splash_Screen value to "disabled", "false", "no" or 0. The value may also be set to the minimum number of seconds that the splash screen should remain visible; not less than three seconds (unless 0) is allowed.
Manager_Width
The width of the Manager window (pixels). Default: 750.
Manager_Height
The height of the Manager window (pixels). Default: 463.
Manager_Location_X
The initial horizontal screen position of the Manager window. Default: 300.
Manager_Location_Y
The initial vertical screen position of the Manager window. Default: 100.
Monitor_Width
The width of the log stream monitor pane (pixels). Default: 700.
Monitor_Height
The height of the loc stream monitor pane (pixels). Default: 400.
Tooltips
If set to "enabled", "true", "yes" or 1 (case insensitive) GUI component tooltips will be enabled on startup. Default: enabled.

@author Bradford Castalia - UA/PIRL @version 1.55 */ public class Manager extends JFrame implements Processing_Listener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Manager (1.55 2012/04/16 06:04:10)"; // Configuration: /** Configuration parameters. */ public static final String // Manager: SPLASH_SCREEN_PARAMETER_NAME = "Splash_Screen", MANAGER_WIDTH_PARAMETER_NAME = "Manager_Width", MANAGER_HEIGHT_PARAMETER_NAME = "Manager_Height", MANAGER_LOCATION_X_PARAMETER_NAME = "Manager_Location_X", MANAGER_LOCATION_Y_PARAMETER_NAME = "Manager_Location_Y", TOOLTIPS_PARAMETER_NAME = "Tooltips", // Log Monitor: MONITOR_WIDTH_PARAMETER_NAME = "Monitor_Width", MONITOR_HEIGHT_PARAMETER_NAME = "Monitor_Height"; /** Class-relative name of the directory containing icon/image files. */ public static final String ICONS_DIRECTORY = "Icons"; /** Minimum splash screen display time (seconds) if enabled. */ public static final int SPLASH_SCREEN_MINIMUM_TIME = 3; private int Splash_Screen_Time = SPLASH_SCREEN_MINIMUM_TIME; private static final String SPLASH_SCREEN_IMAGE_FILENAME = "Conductor_Splash.png"; private int Manager_Width = 750, Manager_Height = 463, Manager_Location_X = 300, Manager_Location_Y = 100; // Conductor: private String Conductor_Identification; // The Conductor being managed, or its remote proxy. private Management The_Management = null; // The Conductor configuration. private Configuration Conductor_Configuration = null; private Parameter_View Configuration_View = null; // Sources: private Sources_Table Sources_Table; private Sources_Table_Model Sources = new Sources_Table_Model (Conductor.SOURCES_FIELD_NAMES); public static final int DEFAULT_MAX_SOURCES_ROWS = 20; private static int Default_Max_Sources_Rows = DEFAULT_MAX_SOURCES_ROWS; private int Max_Sources_Rows = Default_Max_Sources_Rows; // Procedures: private Procedures_Table Procedures_Table; private Procedures_Table_Model Procedures = new Procedures_Table_Model (); // Running, success and failure. private volatile int Processing_State = Conductor.WAITING; public static final int WARNING = -8, EXITING = -9; private volatile boolean Quit_Pending = false, Closing = false; private JButton Processing_Button; private static final String START_LABEL = "Start", WAIT_LABEL = "Wait", STOP_LABEL = "Stop", DONE_LABEL = "Done"; private volatile int Sequential_Failures = 0, Stop_On_Failure = 0; private JLabel Success_Count_Label, Failure_Count_Label, Sequential_Failure_Count_Label, Stop_On_Failure_Label; public static final String SUCCESS_ICON_NAME = "success_medium.gif", FAILURE_ICON_NAME = "error_medium.gif", WARNING_ICON_NAME = "warning_medium.gif"; private static ImageIcon Success_Icon = null, Failure_Icon = null, Warning_Icon = null; private Percent_Bar Success_Count_Bar, Failure_Count_Bar; private Blinker Status_Blinker; private static final int POLLING_BLINKER_RATE = 1000, RUNNING_BLINKER_RATE = 750, WARNING_BLINKER_RATE = 500, FAILURE_BLINKER_RATE = 250; public static final boolean DEFAULT_STATUS_ANNUNCIATOR = true; private JCheckBoxMenuItem Annunciator_Checkbox; // Log Monitor: private JFrame Log_Monitor_Window = null; private Stream_Monitor Log_Monitor = new Stream_Monitor (); public static final boolean DEFAULT_LOG_WHILE_CLOSED = false; private JCheckBoxMenuItem Log_While_Closed_Checkbox; private int Log_Monitor_Width = 700, Log_Monitor_Height = 400; private static final SimpleAttributeSet stdout_STYLE = new SimpleAttributeSet (), stderr_STYLE = new SimpleAttributeSet (); static { StyleConstants.setFontFamily (stdout_STYLE, "Monospaced"); StyleConstants.setBold (stdout_STYLE, true); StyleConstants.setFontFamily (stderr_STYLE, "Monospaced"); StyleConstants.setBold (stderr_STYLE, true); StyleConstants.setBackground (stderr_STYLE, Color.YELLOW); } // Miscellaneous. private JCheckBoxMenuItem Tooltips_Checkbox; private boolean Tooltips_Enabled = true; private JMenu Options_Menu; private JMenuItem Quit_Menu_Item; private static final Toolkit TOOLKIT = Toolkit.getDefaultToolkit (); private static final int MENU_SHORTCUT_KEY_MASK = TOOLKIT.getMenuShortcutKeyMask (); private static final String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_PROCESSING_LISTENER = 1 << 3, DEBUG_PROCESSING_ACTIONS = 1 << 4, DEBUG_PROCESSING_STATE = 1 << 14, DEBUG_GUI_ACTIONS = 1 << 5, DEBUG_SOURCES = 1 << 6, DEBUG_SOURCES_RENDERER = 1 << 7, DEBUG_SOURCES_MODEL = 1 << 8, DEBUG_PROCEDURES = 1 << 9, DEBUG_PROCEDURES_RENDERER = 1 << 10, DEBUG_PROCEDURES_MODEL = 1 << 11, DEBUG_LOG_MONITOR = 1 << 12, DEBUG_ICONS = 1 << 13, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Conductor Manager.

The Configuration object is expected to contain all the necessary information Conductor needs to connect to the database as well as any other Conductor parameters it might use.

@param management A Condcutor_Mangement object that is a Conductor or a proxy - such as a Stage_Manager system - for managing a Conductor. @throws IllegalArgumentException If the management is null. @throws Remote_Management_Exception If a Conductor Management protocol error occurs. This can only happen if the management is not {@link #Local_Management(Management) local} to a Conductor. */ public Manager ( Management management, Configuration configuration ) throws Remote_Management_Exception { super ("Conductor Manager 1.55"); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Manager"); if ((The_Management = management) == null) { if (Local_Management (The_Management)) Dialog_Box.Error ("No Conductor provided to manage!"); throw new IllegalArgumentException (ID + "No Conductor provided to manage!"); } if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Configure the Manager ..."); // Must be done before the Conductor_Configuration is obtained. Configure (configuration); // Use the cross-platform LAF. try {UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ());} catch (Exception exception) {/* Just leave the existing LAF. */} Splash_Screen splash = null; if (Local_Management (The_Management) && Splash_Screen_Time > 0) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Splash_Screen ..."); // Set the location of Icons to be relative to this class. Class class_relative = Icons.Class_Relative (); String icons_directory = Icons.Directory (ICONS_DIRECTORY, this.getClass ()); // Present a splash screen. splash = new Splash_Screen (SPLASH_SCREEN_IMAGE_FILENAME).Start (); // Restore the previous Icons directory. Icons.Directory (icons_directory, class_relative); } else Splash_Screen_Time = 0; if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Conductor_Configuration ..."); // Must be done after Configure but before GUI setup. if ((Conductor_Configuration = The_Management.Configuration ()) == null) { // Provide an empty configuration. try {Conductor_Configuration = new Configuration ((Parameter)null);} catch (Configuration_Exception exception) {/* Shouldn't happen */} } if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Icons ..."); Load_Icons (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Menus ..."); setJMenuBar (Menus ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Panels ..."); getContentPane ().add (Panels ()); setPreferredSize (new Dimension (Manager_Width, Manager_Height)); pack (); setLocation (Manager_Location_X, Manager_Location_Y); setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) {Close ();} public void windowClosed (WindowEvent event) {Close ();} }); // Initialize the log monitor window. if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Log_Window ..."); Log_Window (); // Initialize the Conductor state variables and their GUI displays. if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Report_Processing_State ..."); Report_Processing_State (The_Management.Processing_State ()); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Refresh_Procedure_Records ..."); Refresh_Procedure_Records (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Report_Success_Count ..."); Report_Success_Count (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Set_Stop_on_Failure ..."); Set_Stop_on_Failure (Config_Value (Conductor.STOP_ON_FAILURE_PARAMETER, Stop_On_Failure)); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Report_Sequential_Failures ..."); Report_Sequential_Failures (The_Management.Sequential_Failures ()); if (Splash_Screen_Time > 0) { // Have the splash screen stop and display the GUI. splash .Parent (this) .Duration (Splash_Screen_Time); splash = null; // Dispose of the splash screen. } else setVisible (true); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Add_Processing_Listener ..."); The_Management.Add_Processing_Listener (this); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Manager"); } public Manager ( Management management ) throws Remote_Management_Exception {this (management, null);} private Manager () {} /*============================================================================== Accessors */ public Management Management () {return The_Management;} public int Default_Max_Sources_Rows () {return Default_Max_Sources_Rows;} public Manager Default_Max_Sources_Rows ( int rows ) { if (rows < DEFAULT_MAX_SOURCES_ROWS) rows = DEFAULT_MAX_SOURCES_ROWS; Default_Max_Sources_Rows = rows; return this; } public int Max_Sources_Rows () {return Max_Sources_Rows;} public Manager Max_Sources_Rows ( int rows ) { if (rows < DEFAULT_MAX_SOURCES_ROWS) rows = DEFAULT_MAX_SOURCES_ROWS; Max_Sources_Rows = rows; return this; } /*============================================================================== Configuration */ private void Configure ( Configuration configuration ) { /* Temporary use of Conductor_Configuration. The Config_Value methods presume the use of the Conductor_Configuration. Obviously it is critical that this method be called before the Conductor_Configuration is obtained from The_Management. */ if ((Conductor_Configuration = configuration) == null) // Nothing to configure. return; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Manager.Configure -" + NL + Conductor_Configuration.Description ()); int value; Manager_Location_X = Config_Value (MANAGER_LOCATION_X_PARAMETER_NAME, Manager_Location_X); Manager_Location_Y = Config_Value (MANAGER_LOCATION_Y_PARAMETER_NAME, Manager_Location_Y); value = Config_Value (MANAGER_WIDTH_PARAMETER_NAME, Manager_Width); if (value < 10) value = 10; Manager_Width = value; value = Config_Value (MANAGER_HEIGHT_PARAMETER_NAME, Manager_Height); if (value < 10) value = 10; Manager_Height = value; value = Config_Value (MONITOR_WIDTH_PARAMETER_NAME, Log_Monitor_Width); if (value < 10) value = 10; Log_Monitor_Width = value; value = Config_Value (MONITOR_HEIGHT_PARAMETER_NAME, Log_Monitor_Height); if (value < 10) value = 10; Log_Monitor_Height = value; // Splash screen display. Splash_Screen_Time = Config_Value (SPLASH_SCREEN_PARAMETER_NAME, -1); if (Splash_Screen_Time < 0) { // Try for a boolean value. if (Config_Flag (SPLASH_SCREEN_PARAMETER_NAME, false)) Splash_Screen_Time = SPLASH_SCREEN_MINIMUM_TIME; else Splash_Screen_Time = 0; } else if (Splash_Screen_Time > 0 && Splash_Screen_Time < SPLASH_SCREEN_MINIMUM_TIME) Splash_Screen_Time = SPLASH_SCREEN_MINIMUM_TIME; Tooltips_Enabled = Conductor_Configuration.Enabled (TOOLTIPS_PARAMETER_NAME, Tooltips_Enabled); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Manager.Configure"); } /** Get the configuration pathname for a parameter name.

If the name is not an {@link Configuration#Is_Absolute_Pathname(String) absolute pathname} the {@link Conductor#CONDUCTOR_GROUP} name is prepended to the name to form an absolute pathname.

@param name A parameter name String. @return The possibily modified abosulte pathname String. */ public String Config_Pathname ( String name ) { if (name != null && Conductor_Configuration != null && ! Configuration.Is_Absolute_Pathname (name)) return Conductor_Configuration.Path_Delimiter () + Conductor.CONDUCTOR_GROUP + Conductor_Configuration.Path_Delimiter () + name; return name; } /** Get the String value of a configuration parameter.

The first value of a Parameter with an Array Value will be returned.

@param name The name of the parameter from which to obtain the value. If the name may be {@link #Config_Pathname(String) qualified} before use. @return A String value. This will be null if the parameter could not be found in the Conductor configuration. */ public String Config_Value ( String name ) { if (Conductor_Configuration == null) return null; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">-< Config_Value: " + name + " = " + Conductor_Configuration.Get_One (Config_Pathname (name))); return Conductor_Configuration.Get_One (Config_Pathname (name)); } private int Config_Value ( String name, int default_value ) { name = Config_Value (name); if (name != null) { try {return Integer.parseInt (name);} catch (NumberFormatException exception) {} } return default_value; } private boolean Config_Flag ( String name, boolean default_value ) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">-< Config_Flag: " + name + " = " + ((Conductor_Configuration == null) ? default_value : Conductor_Configuration.Enabled (Config_Pathname (name), default_value))); if (Conductor_Configuration == null) return default_value; return Conductor_Configuration.Enabled (Config_Pathname (name), default_value); } private void Report_Configuration_Change ( Configuration configuration ) { Conductor_Configuration = configuration; Set_Stop_on_Failure (Config_Value (Conductor.STOP_ON_FAILURE_PARAMETER, Stop_On_Failure)); if (Configuration_View != null && Configuration_View.isVisible ()) Configuration_View.Parameter_Pane ().Parameter (Conductor_Configuration); } /*============================================================================== GUI */ private JMenuBar Menus () { JMenuBar menu_bar = new JMenuBar (); JMenu menu; JMenuItem menu_item; // File. menu = new JMenu ("File"); Quit_Menu_Item = new JMenuItem ("Quit"); Quit_Menu_Item.setMnemonic (KeyEvent.VK_Q); Quit_Menu_Item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_Q, MENU_SHORTCUT_KEY_MASK)); Quit_Menu_Item.addActionListener (new ActionListener() {public void actionPerformed (ActionEvent event) {Quit_Conductor ();}}); if (! Local_Management (The_Management)) { Quit_Menu_Item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_Q, ActionEvent.ALT_MASK)); menu_item = new JMenuItem ("Close"); menu_item.setMnemonic (KeyEvent.VK_C); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_W, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener() {public void actionPerformed (ActionEvent event) {Close ();}}); menu.add (menu_item); } menu.add (Quit_Menu_Item); menu_bar.add (menu); // Options. Options_Menu = new JMenu ("Options"); menu_item = new JMenuItem ("Polling Interval ..."); menu_item.setMnemonic (KeyEvent.VK_P); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_P, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Set_Poll_Interval ();}}); Options_Menu.add (menu_item); menu_item = new JMenuItem ("Default Value ..."); menu_item.setMnemonic (KeyEvent.VK_V); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_V, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Set_Default_Value ();}}); Options_Menu.add (menu_item); menu_item = new JMenuItem ("Stop On Failure ..."); menu_item.setMnemonic (KeyEvent.VK_F); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_F, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Set_Stop_on_Failure ();}}); Options_Menu.add (menu_item); menu_item = new JMenuItem ("Reset Sequential Failures"); menu_item.setMnemonic (KeyEvent.VK_R); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_R, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Reset_Sequential_Failures ();}}); Options_Menu.add (menu_item); menu_bar.add (Options_Menu); // View: menu = new JMenu ("View"); menu_item = new JMenuItem ("Configuration"); menu_item.setMnemonic (KeyEvent.VK_C); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_C, ActionEvent.ALT_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {View_Configuration ();}}); menu.add (menu_item); menu_item = new JMenuItem ("Log Monitor"); menu_item.setMnemonic (KeyEvent.VK_L); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_L, ActionEvent.ALT_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Show_Log_Monitor ();}}); menu.add (menu_item); Tooltips_Checkbox = new JCheckBoxMenuItem ("Tooltips", Tooltips_Enabled); ToolTipManager.sharedInstance ().setEnabled (Tooltips_Enabled); Tooltips_Checkbox.setMnemonic (KeyEvent.VK_T); Tooltips_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {ToolTipManager.sharedInstance ().setEnabled (Tooltips_Enabled = ! Tooltips_Enabled);}}); menu.add (Tooltips_Checkbox); Annunciator_Checkbox = new JCheckBoxMenuItem ("Annunciator", DEFAULT_STATUS_ANNUNCIATOR); Annunciator_Checkbox.setMnemonic (KeyEvent.VK_A); Annunciator_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Status_Blinking ();}}); menu.add (Annunciator_Checkbox); menu.addSeparator (); menu_item = new JMenuItem ("Max Sources ..."); menu_item.setMnemonic (KeyEvent.VK_S); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_S, ActionEvent.ALT_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Set_Max_Sources ();}}); menu.add (menu_item); menu_bar.add (menu); return menu_bar; } private JPanel Panels () { JPanel panel = new JPanel (new GridBagLayout ()), sources_panel, procedures_panel; GridBagConstraints location = new GridBagConstraints (); // Pipeline identification: location.anchor = GridBagConstraints.WEST; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 5, 5, 5); panel.add (Identification_Panel (), location); // Sources and Procedures tables. JSplitPane split_pane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, Sources_Panel (), Procedures_Panel ()); split_pane.setOneTouchExpandable (true); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.insets = new Insets (0, 5, 5, 5); panel.add (split_pane, location); // Counts and Control. location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weighty = 0.0; panel.add (Counts_and_Control_Panel (), location); return panel; } private JPanel Identification_Panel () { JPanel panel = new JPanel (); Conductor_Identification = Config_Value (Conductor.CATALOG_PARAMETER) + '.' + Config_Value (Conductor.PIPELINE_PARAMETER) + " on " + Config_Value (Conductor.CONDUCTOR_ID_PARAMETER); panel.add (new JLabel ("Pipeline: " + Conductor_Identification)); setTitle ("Conductor Manager - " + Conductor_Identification); return panel; } /*------------------------------------------------------------------------------ Status Counts and Control */ private JPanel Counts_and_Control_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Counts. location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.insets = new Insets (0, 5, 5, 5); panel.add (Status_Counts_Panel (), location); // Processing control. Processing_Button = new JButton (START_LABEL); Processing_Button.setOpaque (true); Processing_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {Processing_Action ();}}); Status_Blinker = new Blinker (Processing_Button, Colors.RUNNING_BLINKER, Blinker.INDEFINITELY, 0, RUNNING_BLINKER_RATE); getRootPane ().setDefaultButton (Processing_Button); location.weightx = 0.0; location.weighty = 0.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Processing_Button, location); return panel; } private JPanel Status_Counts_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); JLabel label; int width, height; // Success. if (Success_Icon != null) { label = new JLabel (Success_Icon); width = Success_Icon.getIconWidth (); width += width / 2; height = Success_Icon.getIconHeight (); location.insets = new Insets (0, 5, 5, 5); } else { label = new JLabel ("Success:"); label.setOpaque (true); label.setBackground (Colors.SUCCESS); width = label.getWidth (); height = label.getHeight (); location.insets = new Insets (0, 5, 5, 0); } label.setToolTipText ("Source records successfully processed"); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.BOTH; location.weightx = 0.0; location.weighty = 1.0; panel.add (label, location); Success_Count_Label = new JLabel ("0", JLabel.RIGHT); Success_Count_Label.setMinimumSize (new Dimension (width, height)); Success_Count_Label.setOpaque (true); Success_Count_Label.setBackground (Colors.SUCCESS); Success_Count_Label.setToolTipText ("Source records successfully processed: Total count"); location.insets = new Insets (0, 0, 5, 0); panel.add (Success_Count_Label, location); Success_Count_Bar = new Percent_Bar (); Success_Count_Bar.setForeground (Colors.RUNNING_BLINKER); Success_Count_Bar.setOpaque (false); Success_Count_Bar.setPreferredSize (new Dimension (50, height)); Success_Count_Bar.setToolTipText ("Source records successfully processed: Percent of all processed"); location.anchor = GridBagConstraints.WEST; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Success_Count_Bar, location); // Failure. if (Failure_Icon != null) { label = new JLabel (Failure_Icon); location.insets = new Insets (0, 5, 5, 5); } else { label = new JLabel ("Failure:"); label.setOpaque (true); label.setBackground (Colors.FAILURE); location.insets = new Insets (0, 5, 5, 0); } label.setToolTipText ("Source records failed processing"); location.anchor = GridBagConstraints.EAST; location.weightx = 0.0; location.gridwidth = 1; panel.add (label, location); Failure_Count_Label = new JLabel ("0", JLabel.RIGHT); Failure_Count_Label.setOpaque (true); Failure_Count_Label.setBackground (Colors.FAILURE); Failure_Count_Label.setMinimumSize (new Dimension (width, height)); Failure_Count_Label.setToolTipText ("Source records failed processing: Total count"); location.insets = new Insets (0, 0, 5, 0); panel.add (Failure_Count_Label, location); Failure_Count_Bar = new Percent_Bar (); Failure_Count_Bar.setForeground (Colors.FAILURE_BLINKER); Failure_Count_Bar.setOpaque (false); Failure_Count_Bar.setPreferredSize (new Dimension (50, height)); Failure_Count_Bar.setToolTipText ("Source records failed processing: Percent of all processed"); location.anchor = GridBagConstraints.WEST; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Failure_Count_Bar, location); // Sequential. location.anchor = GridBagConstraints.EAST; location.weightx = 0.0; location.gridwidth = 1; if (Warning_Icon != null) { location.insets = new Insets (0, 5, 5, 5); label = new JLabel (Warning_Icon); label.setToolTipText ("Source records sequentially failed processing"); panel.add (label, location); } else panel.add (Box.createHorizontalGlue (), location); Sequential_Failure_Count_Label = new JLabel (String.valueOf (Sequential_Failures), JLabel.RIGHT); Sequential_Failure_Count_Label.setOpaque (true); Sequential_Failure_Count_Label.setBackground (Colors.WARNING); Sequential_Failure_Count_Label.setMinimumSize (new Dimension (width, height)); Sequential_Failure_Count_Label.setToolTipText ("Source records sequentially failed processing: Count"); location.insets = new Insets (0, 0, 5, 0); panel.add (Sequential_Failure_Count_Label, location); Stop_On_Failure_Label = new JLabel ("/" + Stop_On_Failure + " Sequential"); Stop_On_Failure_Label.setOpaque (true); Stop_On_Failure_Label.setBackground (Colors.WARNING); Stop_On_Failure_Label.setToolTipText ("Source records sequentially failed processing: Stop at amount"); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.VERTICAL; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Stop_On_Failure_Label, location); return panel; } /*------------------------------------------------------------------------------ Sources */ private JPanel Sources_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); panel.setBorder (BorderFactory.createTitledBorder ("Sources")); Sources_Table = new Sources_Table (Sources); JScrollPane scroll_pane = new JScrollPane (Sources_Table); scroll_pane.setPreferredSize (new Dimension (100, 100)); scroll_pane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scroll_pane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (scroll_pane, location); return panel; } //.............................................................................. private void Sources_Changed ( int row ) { if (row >= Sources.getRowCount ()) return; if (row < 0) Sources.fireTableRowsUpdated (0, Sources.getRowCount () - 1); else Sources.fireTableRowsUpdated (row, row); } private void Report_Source_Record ( Vector record ) { if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (">>> Manager.Report_Source_Record: " + record); if (record == null) { if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println ("<<< Manager.Report_Source_Record"); return; } try { if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Getting record field " + Conductor.SOURCE_NUMBER_FIELD); String source_number = (String)record.get (Conductor.SOURCE_NUMBER_FIELD); if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Record source number: " + source_number + NL +" Sources (" + Sources.Current_Row + ',' + Conductor.SOURCE_NUMBER_FIELD + ") field: " + (String)Sources.getValueAt (Sources.Current_Row, Conductor.SOURCE_NUMBER_FIELD)); if (Sources.Current_Row >= 0 && source_number .equals ((String)Sources.getValueAt (Sources.Current_Row, Conductor.SOURCE_NUMBER_FIELD))) { // Update the source record. if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Update Sources record " + Sources.Current_Row); Sources.getDataVector ().set (Sources.Current_Row, record); Sources_Changed (Sources.Current_Row); } else { // New source record. Sources.Current_Row++; if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Source_Row: " + Sources.Current_Row); if (Sources.Current_Row == Max_Sources_Rows) { if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Removing Sources record 0"); Sources.removeRow (0); Sources.Current_Row--; Sources_Changed (Sources.Current_Row - 1); } if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println (" Adding Sources record " + Sources.Current_Row); Sources.addRow (record); // Auto-scroll the table if the previous last row was visible. int row = Sources.getRowCount () - 2; if (row < 0 || Sources_Table.Table_Row_Is_Visible (Sources_Table, row)) Sources_Table.Show_Table_Row (Sources_Table, ++row); } } catch (ArrayIndexOutOfBoundsException exception) { // This shouldn't happen if the record that was sent is valid. if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println ("!!! " + exception); } if ((DEBUG & DEBUG_SOURCES) != 0) System.out.println ("<<< Manager.Report_Source_Record"); } /** Update the success/failure status reporting.

Every time a source record completes processing the sequential failures count is reported. If the source record completed processing successfully the sequential failures count will be set to zero. The count will also be set to zero when Conductor acknowledges a {@link #Reset_Sequential_Failures()) request. */ private void Report_Sequential_Failures ( int sequential_failures ) { if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println (">>> Manager.Report_Sequential_Failures: " + sequential_failures); if ((Sequential_Failures = sequential_failures) == 0) { Sequential_Failure_Count_Label.setText ("0"); Stop_On_Failure_Label.setText ("/" + Stop_On_Failure + " Sequential"); if (Processing_State > 0) // While running. Report_Success_Count (); else if (Status_Blinker.isRunning ()) { // While stopped. Status_Blinker.stop (); Reset_Procedures_Table (); } } else { Report_Success_Count (); Sequential_Failure_Count_Label.setText (String.valueOf (Sequential_Failures)); Stop_On_Failure_Label.setText ("/" + Stop_On_Failure + " Sequential"); } Status_Blinker_State (); if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println ("<<< Manager.Report_Sequential_Failures"); } private void Report_Success_Count () { if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println (">>> Manager.Report_Success_Count"); String success_string = Config_Value (Conductor.SOURCE_SUCCESS_COUNT); if (success_string == null) return; String failure_string = Config_Value (Conductor.TOTAL_FAILURE_COUNT); if (failure_string == null) return; int success_count, failure_count; try { success_count = Integer.parseInt (success_string); failure_count = Integer.parseInt (failure_string); } catch (NumberFormatException exception) {return;} Success_Count_Label.setText (success_string); Failure_Count_Label.setText (failure_string); Success_Count_Bar.Percent ((100.0 * success_count) / (success_count + failure_count)); Failure_Count_Bar.Percent (100.0 - Success_Count_Bar.Percent ()); if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println (" Success_Count = " + success_count + NL +" Failure_Count = " + failure_count + NL +" Success % = " + Success_Count_Bar.Percent () + NL +" Failure % = " + Failure_Count_Bar.Percent () + NL +"<<< Manager.Report_Success_Count"); } private void Report_Error_Condition ( String report ) { if (report == null) return; TOOLKIT.beep (); new Error_Report (Conductor_Identification, "The Conductor reported an error condition.", report, this); } /*------------------------------------------------------------------------------ Procedures */ private JPanel Procedures_Panel () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); panel.setBorder (BorderFactory.createTitledBorder ("Procedures")); Procedures_Table = new Procedures_Table (Procedures); JScrollPane scroll_pane = new JScrollPane (Procedures_Table); scroll_pane.setPreferredSize (new Dimension (100, 50)); scroll_pane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scroll_pane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; panel.add (scroll_pane, location); return panel; } //.............................................................................. /** Refresh the table of procedures.

The current table of procedures is obtained from the Management. This table is expected to have its records sorted and its fields ordered. */ private void Refresh_Procedure_Records () { if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (">>> Manager.Refresh_Procedure_Records"); // Get the procedures table. Vector table; try {table = The_Management.Procedures ();} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to refresh the procedure records.", exception, this); return; } if ((DEBUG & DEBUG_PROCEDURES) != 0) { System.out.println (" " + (table.size () - 1) + " procedure records -"); Iterator records = table.iterator (); while (records.hasNext ()) System.out.println ((Vector)records.next ()); } if (table.size () == 0) // No procedures. { new Error_Report ("No Procedures", "An empty procedures table was obtained.", this); return; } Vector field_names = (Vector)table.remove (0); synchronized (Procedures) { // Automatically generates a table snapshot. Procedures.setDataVector (table, field_names); Procedures.Current_Row = -1; } if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (" Procedures.Current_Row = -1" + NL +"<<< Manager.Refresh_Procedure_Records"); } private void Report_Procedure_Record ( Vector record ) { if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (">>> Manager.Report_Procedure_Record: " + record); if (record == null) { Reset_Procedures_Table (); if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println ("<<< Manager.Report_Procedure_Record"); return; } if (Config_Value (Conductor.TOTAL_PROCEDURE_RECORDS_PARAMETER, Procedures.getRowCount ()) > Procedures.getRowCount ()) { if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (" " + Conductor.TOTAL_PROCEDURE_RECORDS_PARAMETER + " = " + Config_Value (Conductor.TOTAL_PROCEDURE_RECORDS_PARAMETER) + NL +" Procedures rows = " + Procedures.getRowCount ()); Refresh_Procedure_Records (); if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println ("<<< Manager.Report_Procedure_Record"); return; } int row = Config_Value (Conductor.PROCEDURE_COUNT_PARAMETER, -1) - 1; if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (" row = " + row); if (row < Procedures.Current_Row) // Moved back to a previous procedure. Reset_Procedures_Table (); Procedures.Current_Row = row; if (Procedures.Current_Row >= 0) { // Replace the current record in the table. if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (" Replacing record " + Procedures.Current_Row); try { synchronized (Procedures) { Procedures.Record (Procedures.Current_Row, record); // Auto-scroll the table. Sources_Table.Show_Table_Row (Procedures_Table, Procedures.Current_Row); } } catch (ArrayIndexOutOfBoundsException exception) { /* This shouldn't happen if the record that was sent is valid. It is possible, though very unlikely, that the procedures table changed while the Conductor was stopped and then when it was started again a procedure record report was sent from the Conductor before the new procedures table could be fetched and updated resulting in a procedure record from the new table that is not in the range of the old table. In this case the procedure record report will have no effect. However, after a change in the procedures table the procedure record reports always start from row zero, so the chance of procedure record reports out running the table size before the procedures changed notice can be handled by fetching and updating the table is quite slim. */ if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println ("!!! " + exception); } } if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println ("<<< Manager.Report_Procedure_Record"); } private void Reset_Procedures_Table () { if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (">>> Manager.Reset_Procedures_Table"); if (Procedures.Current_Row >= 0) { // Restore the table snapshot. if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println (" Procedures.Current_Row reset from " + Procedures.Current_Row + " to -1" + NL +" Restoring Procedures"); Procedures.Current_Row = -1; synchronized (Procedures) {Procedures.Restore ();} } if ((DEBUG & DEBUG_PROCEDURES) != 0) System.out.println ("<<< Manager.Reset_Procedures_Table"); } /*------------------------------------------------------------------------------ Log Monitor */ private void Log_Window () { if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println (">>> Manager.Log_Window"); Log_Monitor_Window = new JFrame (Conductor_Identification); if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println (" Log_Monitor setup"); Log_Monitor .Auto_Style (true) .Auto_Style (Conductor.STDOUT_NAME, stdout_STYLE) .Auto_Style (Conductor.STDERR_NAME, stderr_STYLE); // .Write (ID + NL + NL); Log_Monitor.setPreferredSize (new Dimension (Log_Monitor_Width, Log_Monitor_Height)); if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println (" Log_Monitor menus"); JMenuBar menu_bar = new JMenuBar (); JMenu menu = Log_Monitor.File_Menu (); menu.addSeparator (); JMenuItem menu_item = new JMenuItem ("Close"); menu_item.setMnemonic (KeyEvent.VK_C); menu_item.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_W, MENU_SHORTCUT_KEY_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Close_Log_Monitor ();}}); menu.add (menu_item); menu_bar.add (menu); menu = Log_Monitor.View_Menu (); menu.addSeparator (); Log_While_Closed_Checkbox = new JCheckBoxMenuItem ("Log While Closed", DEFAULT_LOG_WHILE_CLOSED); Log_While_Closed_Checkbox.setMnemonic (KeyEvent.VK_L); Log_While_Closed_Checkbox.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_L, MENU_SHORTCUT_KEY_MASK)); Log_While_Closed_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Log_While_Closed (Log_While_Closed_Checkbox.isSelected ());}}); menu.add (Log_While_Closed_Checkbox); menu_bar.add (menu); Log_Monitor_Window.setJMenuBar (menu_bar); if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println (" Log_Monitor panels"); JPanel content = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; content.add (Log_Monitor, location); Log_Monitor_Window.setContentPane (content); Log_Monitor_Window.pack (); if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println (" Log_Monitor window closing actions"); Log_Monitor_Window.setDefaultCloseOperation (JFrame.DO_NOTHING_ON_CLOSE); Log_Monitor_Window.addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) {Close_Log_Monitor ();} public void windowClosed (WindowEvent event) {Close_Log_Monitor ();} }); if ((DEBUG & DEBUG_LOG_MONITOR) != 0) System.out.println ("<<< Manager.Log_Window"); } /*============================================================================== Actions */ private void View_Configuration () { if (Configuration_View == null) { Configuration_View = new Parameter_View (Conductor_Identification, Conductor_Configuration); Configuration_View.setDefaultCloseOperation (JFrame.HIDE_ON_CLOSE); View_Locator locator = new View_Locator () .Offsets (0, 0) .Vertical (View_Locator.TOP | View_Locator.INWARD) .Horizontal (View_Locator.RIGHT | View_Locator.OUTWARD); locator.Relocate (Configuration_View, this); Configuration_View.setVisible (true); } else { Configuration_View.Parameter_Pane ().Parameter (Conductor_Configuration); Configuration_View.setVisible (true); Configuration_View.toFront (); } } /** Processing_Button action method. */ private void Processing_Action () { if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (">>> Manager.Processing_Action: Processing_State " + Processing_State); if (Processing_State > 0) { if (Processing_State == Conductor.RUN_TO_WAIT) { // Clear the stop-pending. if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (" Start to clear run-to-wait"); The_Management.Start (); } else Stop_Conductor (); } else { if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (" Procedures table reset"); Reset_Procedures_Table (); if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (" Start"); try {The_Management.Start ();} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to start Conductor processing.", exception, this); } } if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println ("<<< Manager.Processing_Action"); } public void Stop_Conductor () { if (Processing_State > Conductor.RUN_TO_WAIT) { try {The_Management.Stop ();} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to stop Conductor processing.", exception, this); } } } private void Report_Processing_State ( int processing_state ) { if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (">>> Manager.Report_Processing_State: " + processing_state); Processing_State = processing_state; Status_Blinker_State (); if (Processing_State > 0) { if (Processing_State == Conductor.RUN_TO_WAIT) Processing_Button.setText (WAIT_LABEL); else Processing_Button.setText (STOP_LABEL); } else if (Quit_Menu_Item.isEnabled ()) // i.e. not disabled. { if (Processing_State == Conductor.HALTED) TOOLKIT.beep (); if (Status_Blinker.getDelay () == RUNNING_BLINKER_RATE || Status_Blinker.getDelay () == POLLING_BLINKER_RATE) Status_Blinker.stop (); Processing_Button.setText (START_LABEL); if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println (" Quit_Pending: " + Quit_Pending); if (Quit_Pending) Quit (); } if ((DEBUG & (DEBUG_PROCESSING_STATE | DEBUG_PROCESSING_ACTIONS)) != 0) System.out.println ("<<< Manager.Report_Processing_State"); } private void Status_Blinker_State () { Color color = null; int rate = 0, state = Processing_State; if (state != Conductor.HALTED && Sequential_Failures > 0) state = WARNING; switch (state) { case Conductor.RUN_TO_WAIT: case Conductor.RUNNING: color = Colors.RUNNING_BLINKER; rate = RUNNING_BLINKER_RATE; break; case Conductor.POLLING: color = Colors.POLLING_BLINKER; rate = POLLING_BLINKER_RATE; break; case Conductor.WAITING: break; case Conductor.HALTED: color = Colors.FAILURE_BLINKER; if (Sequential_Failures > 0) rate = FAILURE_BLINKER_RATE; break; case WARNING: color = Colors.WARNING_BLINKER; rate = WARNING_BLINKER_RATE; break; case EXITING: break; } if (rate == 0) Status_Blinker.stop (); else { Status_Blinker.setColor (color); Status_Blinker.setDelay (rate); Status_Blinking (); } } private void Status_Blinking () { if (Annunciator_Checkbox.isSelected ()) { if (Processing_State != Conductor.WAITING || Sequential_Failures > 0) Status_Blinker.start (); } else if (Status_Blinker.isRunning ()) Status_Blinker.stop (); } private void Set_Poll_Interval () { try { String value; while ((value = JOptionPane.showInputDialog (Sources_Table, "Polling interval" + NL +"for new source records (seconds):", String.valueOf (The_Management.Poll_Interval ()))) != null) { try { int interval = Integer.parseInt (value); if (interval <= 0) { interval = 0; if (! Dialog_Box.Confirm ("When all available source records have been processed" + NL +"no polling for additional source records will be done.", Sources_Table)) continue; } The_Management.Poll_Interval (interval); break; } catch (NumberFormatException exception) {Dialog_Box.Notice ("Please enter a numeric value.", Sources_Table);} } } catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to set the poll interval.", exception, this); } } private void Set_Stop_on_Failure () { String value; try { while ((value = JOptionPane.showInputDialog (Sources_Table, "The number of sequential failures" + NL +"at which to stop processing.", String.valueOf (Stop_On_Failure))) != null) { try { int stop_on_failure = Integer.parseInt (value); if (stop_on_failure <= 0) { stop_on_failure = 0; if (! Dialog_Box.Confirm ("Processing will not be stopped" + NL +"regardless of the number of sequential failures.", Sources_Table)); continue; } The_Management.Stop_on_Failure (stop_on_failure); break; } catch (NumberFormatException exception) {Dialog_Box.Notice ("Please enter a numeric value.", Sources_Table);} } } catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to set the sequential failures limit.", exception, this); } } private void Set_Stop_on_Failure ( int stop_on_failure ) { if (stop_on_failure < 0) stop_on_failure = 0; if (stop_on_failure != Stop_On_Failure) Stop_On_Failure_Label.setText ("/" + (Stop_On_Failure = stop_on_failure) + " Sequential"); } private void Reset_Sequential_Failures () { try {The_Management.Reset_Sequential_Failures ();} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to reset sequential failures.", exception, this); return; } } private void Set_Default_Value () { try { String value = The_Management.Resolver_Default_Value (); if (value == null) value = ""; value = JOptionPane.showInputDialog (Procedures_Table, "Default for unresolved values." + NL + NL +"No entry means an error condition." + NL +"Enter '' for the empty string value.", value); if (value == null) return; if (value.length () == 0) value = null; else if (value.equals ("''") || value.equals ("\"\"")) value = ""; The_Management.Resolver_Default_Value (value); } catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to set the default for unresolved values.", exception, this); } } private void Set_Max_Sources () { String value; while ((value = JOptionPane.showInputDialog (Sources_Table, "Maximum number of source file records" + NL +"to be retained in the Sources table:", String.valueOf (Max_Sources_Rows))) != null) { try { int max_sources_rows = Integer.parseInt (value); if (max_sources_rows < 1) max_sources_rows = 1; Max_Sources_Rows = max_sources_rows; break; } catch (NumberFormatException exception) {Dialog_Box.Notice ("Please enter a numeric value.", Sources_Table);} } } /** Signal the Conductor to quit.

If the Conductor is in the running {@link Management#Processing_State() processing state} a confirmation dialog is presented before source processing is aborted.

Caution: If the Conductor is polling for sources it is possible that it will acquire a source and begin processing it before the quit signal is received causing the processing of the source to be aborted. */ public void Quit_Conductor () { if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println (">>> Manager.Quit_Conductor"); if (Processing_State > 0) { int check = Dialog_Box.Check ("Abort the active Conductor?", this); if (check <= 0) { if (check < 0) Quit_Pending = true; return; } } Quit (); // This should generate an Exiting Processing Change. if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println ("<<< Manager.Quit_Conductor"); } /** Close and dispose of this Manager.

If the Conductor Management interface is a Conductor it will be told to {@link #Quit_Conductor() quit}. Otherwise the Conductor {@link Management#Remove_Log_Writer(Writer) Log stream} and {@link Management#Remove_Processing_Listener(Processing_Listener) processing event} listeners are unregistered and the Conductor is left in its current state. */ public void Close () { if (Closing) return; if ((DEBUG & DEBUG_GUI_ACTIONS) != 0) System.out.println (">>> Manager.Close" + NL +" Local_Management: " + Local_Management (The_Management)); if (Local_Management (The_Management)) Quit_Conductor (); else { Closing = true; Disable (); // Dispose of any sub-windows. if (Configuration_View != null) { Configuration_View.setVisible (false); Configuration_View.dispose (); } if (Log_Monitor_Window != null) { Log_Monitor_Window.setVisible (false); Log_Monitor_Window.dispose (); } // Dispose of this window. if ((DEBUG & DEBUG_GUI_ACTIONS) != 0) System.out.println (" Window dispose"); this.dispose (); } if ((DEBUG & DEBUG_GUI_ACTIONS) != 0) System.out.println ("<<< Manager.Close"); } private void Quit () { if ((DEBUG & DEBUG_PROCESSING_ACTIONS) != 0) System.out.println (">-< Quit the Conductor"); try {The_Management.Quit ();} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to tell the Conductor to quit.", exception, this); return; } // Disable Conductor controls. Disable (); } public void Disable () { try { The_Management.Remove_Log_Writer (Log_Monitor.Writer ()); The_Management.Remove_Processing_Listener (this); } catch (Exception exception) {} Options_Menu.setEnabled (false); Quit_Menu_Item.setEnabled (false); Status_Blinker.stop (); Processing_Button.setText (DONE_LABEL); Processing_Button.setEnabled (false); } /*------------------------------------------------------------------------------ Log Monitor */ private void Show_Log_Monitor () { if (! Log_Monitor_Window.isVisible ()) { if (! Log_While_Closed_Checkbox.isSelected ()) { Log_Monitor.Clear (); try {The_Management.Add_Log_Writer (Log_Monitor.Writer ());} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to register this Manager" + NL +"as a Conductor log stream listener.", exception, this); return; } } LOG_MONITOR_LOCATOR.Relocate (Log_Monitor_Window, this); Log_Monitor_Window.setVisible (true); } Log_Monitor_Window.toFront (); } private static final View_Locator LOG_MONITOR_LOCATOR = new View_Locator () .Offsets (0, 0) .Vertical (View_Locator.BOTTOM | View_Locator.OUTWARD) .Horizontal (View_Locator.LEFT | View_Locator.INWARD); private void Close_Log_Monitor () { if (Log_Monitor_Window.isVisible ()) { if (! Log_While_Closed_Checkbox.isSelected ()) { try {The_Management.Remove_Log_Writer (Log_Monitor.Writer ());} catch (Exception exception) {} } Log_Monitor_Window.setVisible (false); } } private void Log_While_Closed ( boolean enable ) { if (enable) { try {The_Management.Add_Log_Writer (Log_Monitor.Writer ());} catch (Remote_Management_Exception exception) { new Error_Report ("Management Protocol Failure", "Failed to register this Manager" + NL +"as a Conductor log stream listener.", exception, this); return; } } else if (! Log_Monitor_Window.isVisible ()) { try {The_Management.Remove_Log_Writer (Log_Monitor.Writer ());} catch (Exception exception) {} } } /*============================================================================== Processing_Listener */ public void Processing_Event_Occurred ( Processing_Event event ) { synchronized (Processing_Event_Queue) {Processing_Event_Queue.add (event);} SwingUtilities.invokeLater (new Runnable () {public void run () {Processing_Event_Disposition ();}}); } private Vector Processing_Event_Queue = new Vector (); private void Processing_Event_Disposition () { if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println (">>> Manager.Processing_Event_Disposition"); // Pull the next Processing_Event from the front of the queue. Processing_Event event; synchronized (Processing_Event_Queue) { /* >>> WARNING <<< No check is being done for an empty queue because Processing_Event_Disposition is run from the event queue after a Processing_Event_Occurred enqueues the delivered event, and only one event is removed from the queue each time Processing_Event_Disposition is run. If this one-to-one relationship between Processing_Event_Occurred and Processing_Event_Disposition is ever changed an empty queue check must be added. */ event = (Processing_Event)Processing_Event_Queue.remove (0); } if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.print (event.Changes); if (event.Changes.Procedures_Changed ()) Refresh_Procedure_Records (); Configuration configuration = event.Changes.Configuration (); if (configuration != null) Report_Configuration_Change (configuration); Vector record = event.Changes.Source_Record (); if (record != null) Report_Source_Record (record); record = event.Changes.Procedure_Record (); if (record != null) Report_Procedure_Record (record); // Report processing state before reporting sequential failures. if (event.Changes.Processing_State () != 0) Report_Processing_State (event.Changes.Processing_State ()); else if (event.Changes.Exiting ()) Report_Processing_State (EXITING); if (event.Changes.Sequential_Failures () >= 0) Report_Sequential_Failures (event.Changes.Sequential_Failures ()); Report_Error_Condition (event.Changes.Error_Condition ()); if ((DEBUG & DEBUG_PROCESSING_LISTENER) != 0) System.out.println ("<<< Manager.Processing_Event_Disposition"); } /*============================================================================== Helpers */ /** Loads the application icons used by the GUI. */ private void Load_Icons () { if (Success_Icon == null) { if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (">>> Manager.Load_Icons"); Icons.Directory ("Icons", this.getClass ()); if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (" Loading icons from " + Icons.Directory ()); Success_Icon = Icons.Load_Icon (SUCCESS_ICON_NAME); Failure_Icon = Icons.Load_Icon (FAILURE_ICON_NAME); Warning_Icon = Icons.Load_Icon (WARNING_ICON_NAME); if ((DEBUG & DEBUG_ICONS) != 0) System.out.println (" The Icons were " + ((Success_Icon == null) ? "not " : "") + "loaded" + NL +"<<< Manager.Load_Icons"); } } /** Test for a Management interface local to a Conductor.

A local Management interface is implemented by the Conductor class; if the management is an instance of the Conductor class it is local.

@param management A Management interface instance to test. @return true if the management is a local Conductor instance; false otherwise. */ public static boolean Local_Management ( Management management ) {return management instanceof Conductor;} } pirl-2.3.8/PIRL/Conductor/Notify-simple.java0000644000175000017500000007453411742733132020474 0ustar mathieumathieu/* Notify PIRL CVS ID: Notify-simple.java,v 1.4 2012/04/16 06:04:10 castalia Exp Copyright (C) 2011-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import PIRL.Utilities.Streams; import PIRL.Strings.String_Buffer; import java.util.*; import java.net.*; import java.io.*; /** Notify is a mechanism for delivering messages to destinations.

Each of Notify's destinations is represented by a {@link java.net.URI} address which specifies the recipient of the message and the protocol to be used to deliver it. The mailto URI, which specifies how to deliver an e-mail message, is typical. If the address does not specify a protocol, it is treated as a mailto URI. If, for any reason, a destination cannot be used during the delivery of the notice, that destination is added to a Vector of undeliverable destinations.

Notify's message consists of text that is delivered to each destination via a {@link java.net.URLConnection}. The message contents may be provided by a resource specifier which may be a local resource - either a system filename or path within an enclosing jar file - or an external resource via a URL. The message may also be specified directly on the command line. The size of the message may be limited to a maximum number of lines and the number of last lines to always be retained in the message body may also be controlled. This is particularly useful for preventing unexpectedly large message files from causing memory exhaustion while still delivering the beginning and end of the message.

For mailto deliveries a From and Reply-To address and a Subject line may be provided.

Note: This is the "simple" version of Notify that uses only JFC classes. It is an alternative to the new version of Notify that uses the javax.mail (JavaMail) external package which then requires the JAF (JavaBeans Activation Framework) at runtime. The new version deals with unfriendly mail servers; the simple version works fine under most circumstances. Substitute this simple version of Notify.java for the new version before compiling the Conductor package if the external dependencies are unnecessary.

@author Christian Schaller - UA/PIRL @version 1.4 */ public class Notify { /*============================================================================== Constants */ // Class ID public static final String ID = "PIRL.Conductor.Notify (1.4 2012/04/16 06:04:10)"; /** Marker for a message source as opposed to a message string. */ public static final char FILENAME_MARKER = '@'; /** Exit status values. */ public static final int SUCCESS = 0, ILLEGAL_SYNTAX = 1, NO_DESTINATION = 2, CANNOT_READ_MESSAGE_FILE = 3; // The destination(s) of the notification message private Vector Destinations = new Vector (), Undeliverable = new Vector (); // The message to be delivered. private String Message = ""; private int Max_Message_Lines = 1000, Max_Last_Lines = 50, Total_Lines = 0; private Vector Last_Lines = new Vector (Max_Last_Lines); private static final String USERNAME = System.getProperty ("user.name"), NL = System.getProperty ("line.separator"); private String From = USERNAME, Reply_To = null, Subject = null; // Debug control private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_PROCESSING = 1 << 1, DEBUG_GET_STREAM = 1 << 2, DEBUG_MESSAGE = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Notify object with the specified destinations. @param destinations The Vector of destinations to which the notification message will be delivered. */ public Notify ( Vector destinations ) {Destinations (destinations);} /** Constructs a Notify object with no destinations. */ public Notify () {} /*============================================================================== Message Header */ /** Gets the list of destinations to which the notice will be delivered.

Notify will attempt to deliver to each of these destinations on the next invocation of the {@link #Deliver()} method. A failed delivery will add that destination to the list of undeliverable destinations. @return the destinations list as a Vector @see #Undeliverable() */ public Vector Destinations () {return Destinations;} /** Sets the list of destinations to which the message will be delivered.

Redundant destination addresses are removed from the list.

Setting the destinations list has the side effect of resetting the list of undeliverable destinations. @param destinations The Vector of destination URIs. @return This Notify object. @see #Undeliverable() */ public Notify Destinations ( Vector destinations ) { Destinations = Remove_Redundancies (destinations); Undeliverable.clear (); return this; } /** Gets the list of destinations to which the notice could not be delivered.

Whenever a {@link #Deliver() Deliver}y cycle is done destinations to which the notice could not be delivered - for whatever reason - are added to a list of undeliverable destinations. @return The Vector of undeliverable destinations. @see #Deliver() */ public Vector Undeliverable () {return Undeliverable;} /** Gets the From address for e-mail deliveries of the notice.

The From address is not used for other types of deliveries. @return The From address String. */ public String From () {return From;} /** Sets the From address for e-mail deliveries of the notice.

The From address is not used for other types of deliveries. Furthermore, if the From address is null, it will not be used at all. In this case, the underlying Java e-mail delivery mechanism (the mailto URL handler) will attempt to use an appropriate From address based on the owner of the Notify process.

Note that there is no checking provided to ensure that the From address is a valid e-mail address. The address should be a regular e-mail address and not a mailto URL. @param from_address The From address String. @return This Notify object. */ public Notify From ( String from_address ) { if ((From = from_address) == null) From = USERNAME; return this; } /** Gets the Reply-To address for e-mail deliveries of the notice.

The Reply-To address is not used for other types of deliveries. @return The Reply-To address String (may be null). */ public String Reply_To () {return Reply_To;} /** Sets the Reply-To address for e-mail deliveries of the notice.

The Reply-To address is not used for other types of deliveries. Furthermore, if the Reply-To address is null, it will not be used at all.

Note that there is no checking provided to ensure that the Reply-To address is a valid e-mail address. The address should be a regular e-mail address and not a mailto URL. @param reply_to_address The Reply-To address String. @return This Notify object. */ public Notify Reply_To ( String reply_to_address ) { Reply_To = reply_to_address; return this; } /** Gets the Subject line for e-mail deliveries of the notice.

The Subject line is not used for other types of deliveries. @return The Subject line String. */ public String Subject () {return Subject;} /** Sets the Subject line for e-mail deliveries of the notice.

The Subject line is not used for other types of deliveries. Furthermore, if the Subject line is null, it will not be used at all. @param subject_line The Subject line String. @return This Notify object. */ public Notify Subject ( String subject_line ) { Subject = subject_line; return this; } /*============================================================================== Message Body */ /** Gets the maximum number of message lines to be delivered.

@return The maximum number of message lines to be delivered. */ public int Max_Message_Lines () {return Max_Message_Lines;} /** Sets the maximum number of message lines to be delivered.

@param max_lines The maximum number of message lines to be delivered. If this is less than or equal to 0, the {@link Integer#MAX_VALUE maximum integer value} will be used. If it is less than {@link #Max_Last_Lines()} then {@link #Max_Last_Lines()} will be lowered to the same value and there will be no initial message lines. @return This Notify object. */ public Notify Max_Message_Lines ( int max_lines ) { if (max_lines <= 0) Max_Message_Lines = Integer.MAX_VALUE; else { Max_Message_Lines = max_lines; if (Max_Last_Lines > max_lines) Max_Last_Lines = max_lines; } return this; } /** Gets the maximum number of message last lines to be delivered.

@return The maximum number of message last lines to be delivered. */ public int Max_Last_Lines () {return Max_Last_Lines;} /** Sets the maximum number of message last lines to be delivered.

@param max_lines The maximum number of message lines to be delivered. If this is less than 0, 0 will be used. If it is greater than {@link #Max_Message_Lines()} then {@link #Max_Message_Lines()} will be used and there will be no initial message lines. @return This Notify object. */ public Notify Max_Last_Lines ( int max_lines ) { if (max_lines < 0) max_lines = 0; else if (max_lines > Max_Message_Lines) Max_Last_Lines = Max_Message_Lines; else Max_Last_Lines = max_lines; return this; } /** Gets the message to be delivered.

The message is composed of two parts: The initial lines of the message and the last lines of the message. No more than {@link #Max_Message_Lines()} user provided lines - where a line is a sequence of characters terminated by a line separator - will be included. If this limit is exceeded additional lines will be dropped; however, the last {@link #Max_Last_Lines()} lines will always be included. In this case the initial lines will be separated from the last lines by a line of the form:

... [number> lines omitted]

where number> is number of user provided lines that were omitted from the message.

@return The message String. */ public String Message () { String the_message; if (Last_Lines.size () == 0) the_message = Message; else { // Add the last lines. StringBuffer message = new StringBuffer (Message); if (Total_Lines > Max_Message_Lines) message.append ("... [" + (Total_Lines - Max_Message_Lines) + " lines omitted]" + NL); Iterator lines = Last_Lines.iterator (); while (lines.hasNext ()) message.append ((String)lines.next ()); the_message = message.toString (); } return the_message; } /** Reads a message from a named source.

The source can be the name of a local file, the full pathname to that file, the name of a file contained within a jar that packages Notify, a file URL that points to a local file, or a URL that points to an external resource.

Only {@link #Max_Message_Lines()} lines - where a line is a sequence of characters terminated by a line separator - will be retained. The last {@link #Max_Last_Lines()} lines from the source will be saved separately so they will be included in the delivered message regardless of the total number of lines available from the source (@see #Message()).

@param source The name of the message file. If null an empty message is used. @param convert_escapes If true, any escape sequences in the message are converted to their character equivalents; otherwise the string is unchanged. @param append If true, the message is appended to any existing message; otherwise the any existing message is replaced. @return This Notify object. @throws IOException If an input stream cannot be obtained from the message source or if there is a problem reading from the input stream. @see Streams#Get_Stream(String) @see String_Buffer#escape_to_special() */ public Notify Message_Source ( String source, boolean convert_escapes, boolean append ) throws IOException { if (source == null) return this; if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Message_Source: " + source + NL +" Append - " + append +" Max message lines - " + Max_Message_Lines + NL +" Max last lines - " + Max_Last_Lines); InputStream message_stream = Streams.Get_Stream (source); if (message_stream == null) throw new IOException (ID + NL +"Unable to access the message input source - " + source); BufferedReader input = new BufferedReader (new InputStreamReader (message_stream)); if (! append) { // Clear the existing message. Total_Lines = 0; Message = ""; Last_Lines.clear (); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- Source lines -" + NL); String line; int lines_read = 0, last_line_threshold = Max_Message_Lines - Max_Last_Lines; while (input.ready ()) { // Get the next message line. line = input.readLine () + NL; if ((DEBUG & DEBUG_MESSAGE) != 0) ++lines_read; if (convert_escapes) // Expand any escape sequences. line = new String_Buffer (line).escape_to_special ().toString (); if (++Total_Lines > last_line_threshold) { // Add to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (line); } else { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.print (line); Message += line; } } if ((DEBUG & DEBUG_MESSAGE) != 0) { if (Last_Lines.size () != 0) { System.out.println ("--- Last lines -"); Iterator lines = Last_Lines.iterator (); while (lines.hasNext ()) System.out.print ((String)lines.next ()); } System.out.println ("--- Total lines:" + NL +" " + lines_read + " source lines read." + NL +" " + Total_Lines + " message lines, first." + NL +" " + Last_Lines.size () + " message lines, last." + NL +"<<< Message_Source"); } return this; } /** Reads a message from a named source.

Any escape sequences are converted and any existing message appended.

@param source The name of the message file. If null the message is set to the empty String. @return This Notify object. @throws IOException If an input stream cannot be obtained from the message source or if there is a problem reading from the input stream. @see #Message_Source(String, boolean, boolean) */ public Notify Message_Source ( String source ) throws IOException {return Message_Source (source, true, true);} /** Sets the message to be delivered.

@param message The message String. A null message is empty. @param convert_escapes If true, any escape sequences in the message are converted to their character equivalents; otherwise the string is unchanged. @param append If true, the message is appended to any existing message; otherwise an existing message is replaced. @return This Notify object. @see String_Buffer#escape_to_special() */ public Notify Message ( String message, boolean convert_escapes, boolean append ) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Message:" +" convert escapes " + convert_escapes +", append " + append); if (! append) { // Clear the existing message. Total_Lines = 0; Message = ""; Last_Lines.clear (); } if (message != null && message.length () != 0) { if (convert_escapes) message = new String_Buffer (message).escape_to_special ().toString (); Append_Message (message); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("<<< Message"); return this; } /** Sets the message to be delivered.

Any escape sequences are expanded. Any existing message is cleared.

@param message The message String to be delivered. A null message is empty. @return This Notify object. @see #Message(String, boolean, boolean) */ public Notify Message ( String message ) {return Message (message, true, false);} /** Append lines from a String to the Message.

@param string The String from which to extract lines. @return The string index of the last character (exclusive) used. This will be 0 if the Message is already full (Total_Lines >= Max_Message_Lines). It will be -1 if the entire string was used. */ private void Append_Message ( String string ) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println (">>> Append_Message: " + Total_Lines + " total lines so far" + NL +" Max message lines - " + Max_Message_Lines + NL +" Max last lines - " + Max_Last_Lines); int last = 0, last_line_threshold = Max_Message_Lines - Max_Last_Lines, NL_length = NL.length (); if (Total_Lines < last_line_threshold) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- MESSAGE"); // Count message lines. for (last = string.indexOf (NL); last >= 0 && ++Total_Lines < last_line_threshold; last = string.indexOf (NL, last + NL_length)); if (last < 0) // Take the entire message. Message += string; else { // Include the final NL. last += NL_length; if (last == string.length ()) { Message += string; last = -1; } else Message += string.substring (0, last); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- MESSAGE: " + Total_Lines + " total lines"); } if (last >= 0) { if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- LAST LINES: " + Last_Lines.size ()); int start = last; for (last = string.indexOf (NL, start); last >= 0; last = string.indexOf (NL, start)) { // Add to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (string.substring (start, last += NL_length)); ++Total_Lines; start = last; } if (start != string.length ()) { // Add unterminated line to last lines list. if (Last_Lines.size () == Max_Last_Lines) // Scroll down. Last_Lines.remove (0); Last_Lines.add (string.substring (start)); ++Total_Lines; } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("--- LAST LINES: " + Last_Lines.size ()); } if ((DEBUG & DEBUG_MESSAGE) != 0) System.out.println ("<<< Append_Message: " + Total_Lines + " total lines so far"); } /*============================================================================== Delivery methods */ /** Delivers the notice to each destination.

The list of undeliverable destinations (destinations for which, for whatever reason, the delivery could not be made) is cleared prior to delivery. @return The number of successful destinations. @see #Undeliverable() */ public int Deliver () { if ((DEBUG & DEBUG_PROCESSING) != 0) System.out.println (">>> Deliver:" + NL +" To: " + Destinations + NL +" From: " + From + NL +" Reply_To: " + Reply_To + NL +" Subject: " + Subject); Undeliverable.clear (); Iterator list = Destinations.iterator (); while (list.hasNext ()) { String destination = (String)list.next (); URI uri = null; try {uri = new URI (destination);} catch (URISyntaxException e) { Undeliverable.add (destination); continue; } URL url = null; try {url = uri.toURL ();} catch (Exception e) { // Attempt it as a mailto: URL try {url = new URL ("mailto:" + destination);} catch (Exception e_2) { Undeliverable.add (destination); continue; } } Deliver_To (url); } if ((DEBUG & DEBUG_PROCESSING) != 0) System.out.println ("<<< Deliver"); return Destinations.size () - Undeliverable.size (); } /** Delivers the notice to each destination.

@param destinations The Vector of desinations. @return The number of successful destinations. @see #Deliver() */ public int Deliver ( Vector destinations ) { Destinations (destinations); return Deliver (); } /** Delivers the message to a single destination.

The destination URL is opened with a {@link java.net.URLConnection}. An {@link java.net.OutputStream} is obtained from the URLConnection. If the OutputStream cannot be obtained, the destination is added to the list of undeliverable destinations along with the reason, if any, for the failure.

If the OutputStream is obtained, the message is written to it. Depending on the type of URL, additional information may be written. For a mailto URL, for example, a To address, a From address, a Reply-To address, and a Subject line may be added. @param url The destination URL. @see #Deliver() @see #Undeliverable() */ private void Deliver_To ( URL url ) { if ((DEBUG & (DEBUG_PROCESSING | DEBUG_MESSAGE)) != 0) System.out.println (">>> Deliver_To: " + url + NL); if ((DEBUG & DEBUG_MESSAGE) != 0) { System.out.println (" To: " + url.getPath () + NL +" From: " + From + NL +((Reply_To != null) ? (" Reply-To: " + Reply_To + NL) : "") +((Subject != null) ? (" Subject: " + Subject + NL) : "") +" Message -" + NL + Message () + NL +"<<< Deliver_To"); return; } try { URLConnection connection = url.openConnection (); OutputStreamWriter stream_writer = new OutputStreamWriter (connection.getOutputStream ()); /* By default, the mailto URLStreamHandler does not supply a value for To:, Reply-To:, or Subject: in the e-mail stream it writes. These will be supplied but only if a mailto URL is being used. Note that the value of "To:" is the path portion of the URL. */ if (url.getProtocol ().equalsIgnoreCase ("mailto")) { stream_writer.write ("To: " + url.getPath () + NL); stream_writer.write ("From: " + From + NL); if (Reply_To != null) stream_writer.write ("Reply-To: " + Reply_To + NL); if (Subject != null) stream_writer.write ("Subject: " + Subject + NL); } stream_writer.write (Message ()); stream_writer.close (); } catch (Exception exception) { String message = url.toString (); if (exception.getMessage () != null) message += NL + "Reason: " + exception.getMessage (); Undeliverable.add (message); } if ((DEBUG & (DEBUG_PROCESSING | DEBUG_MESSAGE)) != 0) System.out.println ("<<< Deliver_To"); } /*============================================================================== Helpers */ /** Removes redundant entries from a Vector. @param vector The Vector to be vetted. @return A copy of the vector without redundant entries. */ private Vector Remove_Redundancies ( Vector vector ) { if (vector == null || vector.isEmpty () || vector.size () == 1) return vector; Vector copy = new Vector (vector.size ()); Iterator entries = vector.iterator (); while (entries.hasNext ()) { Object object = entries.next (); if (! copy.contains (object)) copy.add (object); } return copy; } /*============================================================================== Application main */ /** Processes the command line arguments.

All error messages are written to stderr. If any destinations are undeliverable, they are listed.

The exit status will be one of:

  • 0 - SUCCESS
  • 1 - ILLEGAL_SYNTAX
  • 2 - NO_DESTINATION
  • 3 - CANNOT_READ_MESSAGE_FILE

@param args The array of command line arguments. @see #Usage(int) */ public static void main ( String[] args ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("*** " + ID + " ***"); Notify notifier = new Notify (); Vector destinations = new Vector (); String message = null, from_address = null, reply_to_address = null, subject_line = null; for (int index = 0; index < args.length; index++) { if (args[index].length () > 1 && args[index].charAt (0) == '-') { switch (args[index].charAt (1)) { case 'T': // To case 't': case 'D': // Destination case 'd': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing To address."); Usage (ILLEGAL_SYNTAX); } // Break down comma separated list. for (int from = 0, to = args[index].indexOf (',', from); from < args[index].length (); to = args[index].indexOf (',', from)) { if (to < 0) to = args[index].length (); if (from < to) destinations.add (args[index].substring (from, to)); from = ++to; } break; case 'F': // From case 'f': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing From address."); Usage (ILLEGAL_SYNTAX); } notifier.From (args[index]); break; case 'R': // Reply-to case 'r': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing Reply-To address."); Usage (ILLEGAL_SYNTAX); } notifier.Reply_To (args[index]); break; case 'S': // Subject case 's': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing Subject line."); Usage (ILLEGAL_SYNTAX); } notifier.Subject (args[index]); break; case 'M': // Message case 'm': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing message."); Usage (ILLEGAL_SYNTAX); } if (args[index].length () > 1 && args[index].charAt (0) == FILENAME_MARKER) { try {notifier.Message_Source (args[index].substring (1));} catch (IOException exception) { System.err.println (exception.getMessage ()); System.exit (CANNOT_READ_MESSAGE_FILE); } } else notifier.Message (args[index]); break; case 'L': // Limit case 'l': if (++index == args.length || args[index].charAt (0) == '-') { System.err.println ("Missing message limit value."); Usage (ILLEGAL_SYNTAX); } int limit = 0; try {limit = Integer.parseInt (args[index]);} catch (NumberFormatException exception) { System.err.println ("Invalid limit value: " + args[index]); Usage (ILLEGAL_SYNTAX); } if (args[index - 1].indexOf ('L', 2) > 1 || args[index - 1].indexOf ('l', 2) > 1) notifier.Max_Last_Lines (limit); else notifier.Max_Message_Lines (limit); break; case 'V': // Version case 'v': System.out.println (ID); System.exit (SUCCESS); case 'H': // Help case 'h': Usage (SUCCESS); default: System.err.println ("Unrecognized option: " + args[index]); Usage (ILLEGAL_SYNTAX); } } else destinations.add (args[index]); } if (destinations.isEmpty ()) { System.err.println ("No To address provided."); Usage (NO_DESTINATION); } notifier.Destinations (destinations); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("Delivering -" + NL + "To: " + destinations + NL + "From: " + notifier.From () + NL + "Reply-To: " + notifier.Reply_To () + NL + "Subject: " + notifier.Subject () + NL + "Message:" + NL + notifier.Message ()); notifier.Deliver (); Vector undeliverable = notifier.Undeliverable (); if (! undeliverable.isEmpty ()) { System.err.println ("Undeliverable destination" + (undeliverable.size () > 1 ? "s" : "") + " -"); Iterator list = undeliverable.iterator (); while (list.hasNext ()) System.err.println (list.next ()); } System.exit (SUCCESS); } /** Prints the command line usage.

Usage: Notify <Switches>
  Switches -
    [-To] <address | URI>[,...]
    [-From <address>] (default: username)
    [-Reply-to <address>]
    [-Subject <subject line>]
    [-Message <message> | @<source>]
    [-Limit[_Last] <lines> (default: message 1000, last 50)
    [-Version
    [-Help

At least one destination (To) address must be provided. Each destination is treated as a URI. If the address does not specify a protocol, it is treated as a mailto URI. A comma separated list of addresses may be provided.

The From and Reply-to addresses are optional and only used in association with mailto destinations. By default the user's address, if available, will be used for the From address. If no Reply-to address is provided, none will be used.

The Subject line is optional and only used in association with mailto destinations. If none is provided, none will be used.

To message may be any text. If the message text begins with the {@link #FILENAME_MARKER} ('@') the text without the marker is interpreted as a file or URL resource specifiction. This may be a local resource, either a system filename or path within an enclosing jar file, or it may refer to an external resource via a URL.

Multiple messages may be specified. They will be concatenated in the order they occur on the command line.

The size of the message may be limited to a maximum number of lines. Excess lines will be dropped but the last lines of the message - the number may be specified - will be retained.

N.B.: Command line options are applied in the order in which they are encountered. Thus, for example, message limit values should be set before the message body is specified.

The -Version option causes the class {@link #ID} to be written to stdout followed by a System.exit.

N.B.: This method always results in a System.exit. @param exit_status The exit status code. The usage statement is written to the stdout if the exit_status is {@link #SUCCESS}, otherwise to stderr. */ public static void Usage ( int exit_status ) { String usage = "Usage: Notify" + NL +" [-To]

[...]" + NL +" [-From
] (default: username)" + NL +" [-Reply-to
]" + NL +" [-Subject ]" + NL +" [-Message | @]" + NL +" [-Limit[_Last] ] (default: message 1000, last 50)" + NL +" [-Version]" + NL +" [-Help]"; if (exit_status == SUCCESS) System.out.println (usage); else System.err.println (usage); System.exit (exit_status); } } pirl-2.3.8/PIRL/Conductor/Evaluate0000755000175000017500000000371411652360551016557 0ustar mathieumathieu#!/usr/bin/perl # # Evaluate # # A wrapper for the Conductor.Evaluate utility. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath: # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # CVS ID: Evaluate,v 1.2 2011/10/27 22:55:37 castalia Exp $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Conductor.Evaluate", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Conductor/Pipeline_Source0000755000175000017500000005240111742733132020072 0ustar mathieumathieu#!/usr/bin/perl =pod =head1 NAME B Inserts pathname(s) into a Conductor pipeline Sources table. =head1 SYNOPSIS Pipeline_Source [EBE.]EBE[_Source] EBE | @EBE [...] [-Pipeline [EBE.]EBE] [-Id EBE | @EBE] [-Log EBE | @EBE] [-COnfiguration EBE] [-Server EBE] [-Catalog EBE[.EBE]] [-Always | -Unique[_or_Processed]] [-Verbose | -Quiet] [-Help] =head1 DESCRIPTION For each data source pathname specified on the command line a pipeline source record is interested into the Source_Pathname field of the EBE_Sources table in the EBE of the database EBE. It is an error to specify a pathname for a file that does not exist. If specified a pathname will be dropped if a source record in the pipeline table has the same pathname, or has the same pathname but has not yet been acquired for processing. A Source_ID and/or Log_Pathname field value may also be specified for each or all the source records. A configuration file is required to proved database access information. =head1 OPTIONS AND ARGUMENTS A list of one or more pathnames to be inserted into the Conductor pipeline Sources table Source_Pathname field must be specified. A file containing a list of pathnames may be specified, following an '@' character, instead of, or in addition to, listing individual pathnames on the command line. Relative pathnames are made absolute before insertion by prepending the current working directory. B: The current working directory is the real, canonical, directory pathname; this will not be the same as a logical pathname accessed via soft links. Each pathname is confirmed as referring to an existing regular file. If any pathname refers to a nonexistant file no pipeline source records will be inserted. =over =item -Pipeline [EBE.]EBE The name of the Conductor pipeline to receive Source_Pathname entries. The pipeline name may be qualified by the EBE name, preceeding a '.' delimiter. Default: The command name, or that part of the command name preceding a "_Source" suffix. B: If the command name provides the pipeline name, it may also use the EBE.EBE notation. =item -Id EBE | @EBE If this option is specified the Source_ID field of the source record will be set to the Esource IDE value. Typically, this option, when used, is specified for each EpathnameE specified; each Esource IDE will be paired with a EpathnameE according to the order in which they occur on the command line. If only one Esource IDE is specified for multiple EpathnamesE, then it is applied to all; any other mismatch will cause a syntax error. A file containing a list of source IDs may be specified, following an '@' character, instead of listing individual source IDs on the command line. If no Esource IDE is specified none will be applied. =item -Log EBE | @EBE If this option is specified the Log_Pathname field of the source record will be set to the Elog directoryE value. Typically, this option, when used, is specified for each EpathnameE specified; each Elog directoryE will be paired with a EpathnameE according to the order in which they occur on the command line. If only one Elog directoryE is specified for multiple EpathnamesE, then it is applied to all; any other mismatch will cause a syntax error. An attempt is made to create any log directory that does not exist. A file containing a list of log directories may be specified, following an '@' character, instead of listing individual log directories on the command line. If no Elog directoryE is specified none will be applied. =item -COnfiguration EBE The value of an environment variable named EBE_CONFIG_DIR will be used, if present, as the name of the directory where the configuration file is to be found if the specified configuration filename is not an absolute pathname; otherwise the current working directory will be tried. Default: EBE.conf. =item -Server EBE The configuration file may contain connection information for more than one database. The information for each database is organized by Server name. A Server name corresponds to a parameter group in the confinguration file, with the same name, that contains access information for a database server. Default: The first name in the Server list specified in the configuration. =item -Catalog EBE[.EBE] The name of the database catalog that contains the Conductor pipeline tables. The catalog name may be qualified by the EBE name, following a '.' delimiter. Default: The EBE name in the command name, if present. =item -Always | -Unique[_or_Processed] This option determines acceptable conditions for inserting a source record for a pathname. When -Always is specified all pathnames are always acceptable. When Unique is specified the canonical pathname must be unique for all source records in the table; i.e. the canonical pathname must not be present in the Source_Pathname field of any existing source record. This precludes inserting a duplicate source pathname in the table. When Unique_or_Processed is specified the canonical pathname may already be present but only if the source record has been acquired for processing (its Conductor_ID field is non-NULL). Unacceptable sources are simply dropped along with any corresponding source ID and log directory pathname, if any. Default: Always. Note: While it is acceptable to register the same pathname more than once in a pipeline sources table, some pipelines may fail if the same source is processed more than once. In various circumstances it is acceptable to register a duplicate pathname only if all source records with the same pathname have already been processed. This option allows the conditions when it is acceptable to source a pathname to a pipeline to be specified. =item -Verbose | -Quiet Verbose operation provides details about what is being done. Quiet operation minimizes messages. Default: Quiet. =item -Help The man page for this procedure is listed. =back =head1 Exit Status =over =item E<48> - Success The database table insert(s) completed successfully. =item E<49> - Bad command line syntax A command line syntax usage message will be provided. =item E<50> - No such file A pathname to a non-existant file, or a file that is not a regular file (e.g. a directory pathname may not be used), was specified. This applies to both source and configuration file pathnames. =item E<51> - No log directory A specified log directory does not exist and could not be created. =item E<52> - Database insert(s) failed The database table insert(s) did not complete successfully. A report from the Update_DB procedure will be provided. =item E<53> - Missing arguments file An arguments list file, indicated with a leading '@' character on the command line, could not be found. =item E<54> - Database query failed A database query checking for an existing source pathname failed. A report from the Query_DB procedure will be provided. =item E<55> - No unique source(s) All the source pathnames were found to be unacceptable because they meet the unique, and possibly unprocessed, conditions in the pipeline Sources table. =back =head1 Author Bradford Castalia, UA/PIRL =head1 Copyright Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . =head1 Version 1.34 2012/04/16 06:04:10 =cut # CVS ID: Pipeline_Source,v 1.34 2012/04/16 06:04:10 castalia Exp #=============================================================================== $CVS_ID = 'Pipeline_Source (1.34 2012/04/16 06:04:10)'; ($Command_Name = $0) =~ s|.*/(\S+)$|$1|; print "$Command_Name - $CVS_ID\n"; # Defaults: # Assume that the command name is the pipeline name. $DEFAULT_PIPELINE = $Command_Name; $DEFAULT_PIPELINE =~ s/(.+)_Source$/$1/ if ($DEFAULT_PIPELINE =~ /.+_Source$/); $DEFAULT_CATALOG = "Proc_Test"; $CONFIG_DIR_ENV = "_CONFIG_DIR"; @UPDATE_DB = ("Update_DB"); @QUERY_DB = ("Query_DB"); $PATHNAME_FIELD = "Source_Pathname"; $SOURCE_ID_FIELD = "Source_ID"; $LOG_PATHNAME_FIELD = "Log_Pathname"; $CONDUCTOR_ID_FIELD = "Conductor_ID"; # Exit status values: $SUCCESS = 0; $BAD_SYNTAX = 1; $NO_SUCH_FILE = 2; $NO_LOG_DIRECTORY = 3; $UPDATE_FAILED = 4; $NO_ARGUMENTS_FILE = 5; $QUERY_FAILED = 6; $NO_UNIQUE_SOURCES = 7; #------------------------------------------------------------------------------- # Command line arguments: use Pod::Usage; pod2usage ( -verbose => 0, -exitval => $BAD_SYNTAX ) unless @ARGV; while ($option = shift @ARGV) { # N.B.: Must come before -Catalog option. if ($option =~ /^-[Cc][Oo]/) { # Configuration file. pod2usage ( -message => "$Command_Name: Missing configuration filename.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $Config_Pathname = shift @ARGV; next; } if ($option =~ /^-[Ss]/) { # Server pod2usage ( -message => "$Command_Name: Missing database server name.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $server = shift @ARGV; pod2usage ( -message => "Database server already specified -\n". "$Database_Server\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if $Database_Server && ($Database_Server ne $server); $Database_Server = $server; next; } if ($option =~ /^-[Cc]/ || $option =~ /^-[Dd]/) { # Catalog (or Database). pod2usage ( -message => "$Command_Name: Missing database catalog name.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $catalog = catalog_name ($option = shift @ARGV); $catalog = $option unless $catalog; Multiple_Catalogs: pod2usage ( -message => "$Command_Name: Multiple catalogs specified - ". "$Catalog and $catalog\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if $Catalog && ($Catalog ne $catalog); $Catalog = $catalog; # Check for pipeline name. $pipeline = pipeline_name ($option); goto Multiple_Pipelines if $pipeline && $Pipeline && ($pipeline ne $Pipeline); $Pipeline = $pipeline if $pipeline; next; } if ($option =~ /^-[Pp]/) { # Pipeline name. pod2usage ( -message => "$Command_Name: Missing pipeline name.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $pipeline = pipeline_name ($option = shift @ARGV); $pipeline = $option unless $pipeline; Multiple_Pipelines: pod2usage ( -message => "$Command_Name: Multiple pipelines specified - ". "$Pipeline and $pipeline\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if $Pipeline && ($Pipeline ne $pipeline); $Pipeline = $pipeline; # Check for catalog name. $catalog = catalog_name ($option); goto Multiple_Catalogs if $catalog && $Catalog && ($catalog ne $Catalog); $Catalog = $catalog if $catalog; next; } if ($option =~ /^-[Ii]/) { # Source ID. pod2usage ( -message => "$Command_Name: Missing source ID.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $arguments = shift @ARGV; if (substr ($arguments, 0, 1) eq '@') { if (! open (ARGUMENTS, '<', substr ($arguments, 1))) { print STDERR "$Command_Name: Unable to open the source ID list file - ", substr ($arguments, 1), "\n"; exit ($NO_ARGUMENTS_FILE); } while () { push @IDs, split; } close ARGUMENTS; } else { push @IDs, $arguments; } next; } if ($option =~ /^-[Ll]/) { # Log Pathname. pod2usage ( -message => "$Command_Name: Missing log file directory.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless (@ARGV && $ARGV[0] !~ /^-/); $arguments = shift @ARGV; if (substr ($arguments, 0, 1) eq '@') { if (! open (ARGUMENTS, '<', substr ($arguments, 1))) { print STDERR "$Command_Name: Unable to open the log directories list file - ", substr ($arguments, 1), "\n"; exit ($NO_ARGUMENTS_FILE); } while () { push @Log_pathnames, split; } close ARGUMENTS; } else { push @Log_pathnames, $arguments; } next; } if ($option =~ /^-[Aa]/) { # Always. $Unique = 0; $Processed = 0; next; } if ($option =~ /^-[Uu]/) { # Unique. $Unique = 1; $Processed = 1 if ($option =~ /.*[Pp]/); next; } if ($option =~ /^-[Vv]/) { # Verbose. $Verbose = 1; next; } if ($option =~ /^-[Qq]/) { # Quiet. $Verbose = 0; next; } # Help. pod2usage ( -verbose => 2, -exitval => $SUCCESS ) if ($option =~ /^-[Hh]/); pod2usage ( -message => "$Command_Name: Unknown option \"$option\"\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if ($option =~ /^-/); # Source pathname. if (substr ($option, 0, 1) eq '@') { if (! open (ARGUMENTS, '<', substr ($option, 1))) { print STDERR "$Command_Name: Unable to open the source pathnames list file - ", substr ($option, 1), "\n"; exit ($NO_ARGUMENTS_FILE); } while () { push @Filenames, split; } close ARGUMENTS; } else { push @Filenames, $option; } } #------------------------------------------------------------------------------- # Post-command initialization. if ($Verbose) { push @QUERY_DB, '-verbose'; push @UPDATE_DB, '-verbose'; } # Pipeline name. if (! $Pipeline) { $pipeline = pipeline_name ($DEFAULT_PIPELINE); $pipeline = $DEFAULT_PIPELINE unless $pipeline; $Pipeline = $pipeline; # Check for catalog name. $catalog = catalog_name ($DEFAULT_PIPELINE); goto Multiple_Catalogs if $catalog && $Catalog && ($catalog ne $Catalog); $Catalog = $catalog if $catalog; } # Database catalog name. if (! $Catalog) { # Try to find a catalog name in the default pipeline name. $Catalog = catalog_name ($DEFAULT_PIPELINE); pod2usage ( -message => "$Command_Name: A database catalog name must be specified.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless $Catalog; } # Configuration file. $Config_Pathname = $Pipeline.".conf" unless $Config_Pathname; use File::Spec; if (! File::Spec->file_name_is_absolute ($Config_Pathname)) { # Relative pathname. # Check for a configuration directory environment variable. $Configuration_Dir = $ENV{$Pipeline . $CONFIG_DIR_ENV}; $Config_Pathname = "$Configuration_Dir/$Config_Pathname" if $Configuration_Dir; # Ensure absolute pathname. $Config_Pathname = File::Spec->rel2abs ($Config_Pathname); } push @QUERY_DB, '-config', $Config_Pathname; push @UPDATE_DB, '-config', $Config_Pathname; # Server selection. if ($Database_Server) { push @QUERY_DB, '-server', $Database_Server; push @UPDATE_DB, '-server', $Database_Server; } # Check the filenames. pod2usage ( -message => "$Command_Name: At least one source pathname must be specified.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) unless @Filenames; pod2usage ( -message => "$Command_Name: The number of source pathnames -\n" . scalar @Filenames . "\n" . "- and source IDs -\n" . scalar @IDs . "\n" . "- do not match.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if (@IDs && @IDs != 1 && @IDs != @Filenames); $ID = shift @IDs if @IDs == 1; pod2usage ( -message => "$Command_Name: The number of source pathnames -\n" . scalar @Filenames . "\n" . "- and log pathnames -\n" . scalar @Log_pathnames . "\n" . "- do not match.\n", -verbose => 0, -exitval => $BAD_SYNTAX ) if (@Log_pathnames && @Log_pathnames != 1 && @Log_pathnames != @Filenames); $Log_pathname = shift @Log_pathnames if @Log_pathnames == 1; foreach $filename (@Filenames) { # Ensure absolute (canonical) pathname. $filename = File::Spec->rel2abs ($filename); if (! -f $filename) { print STDERR "$Command_Name: No such file - $filename\n"; exit ($NO_SUCH_FILE); } # Check for acceptable source pathname. if (Accept_Source ($filename)) { push @Pathnames, $filename; if ($ID) {push @Source_IDs, $ID;} elsif (@IDs) {push @Source_IDs, shift @IDs;} if ($Log_pathname) {push @Logs, $Log_pathname;} elsif (@Log_pathnames) {push @Logs, shift @Log_pathnames;} } else { print "Not unique"; print " or processed" if $Processed; print ": $filename\n"; } } if (! @Pathnames) { print "No unique"; print " or processed" if $Processed; print " sources.\n"; exit ($NO_UNIQUE_SOURCES); } foreach $pathname (@Logs) { # Ensure absolute pathname. $pathname = File::Spec->rel2abs ($pathname); exit ($NO_LOG_DIRECTORY) if (! Make_Path ($pathname)); push @Log_directories, $pathname; } #------------------------------------------------------------------------------- # Database update operation. push @UPDATE_DB, '@-'; print "\n@UPDATE_DB\n" if $Verbose; if (! open UPDATE, "| @UPDATE_DB") { print STDERR "$Command_Name: Unable to open a pipe to Update_DB\n"; print STDERR "@UPDATE_DB\n" unless $Verbose; exit ($UPDATE_FAILED); } foreach $Pathname (@Pathnames) { # Send the update set arguments. print UPDATE '-Table ', $Catalog.'.'.$Pipeline."_Sources ", "$PATHNAME_FIELD=$Pathname\n"; print UPDATE "$SOURCE_ID_FIELD=".shift (@Source_IDs)."\n" if @Source_IDs; print UPDATE "$LOG_PATHNAME_FIELD=".shift (@Log_directories)."\n" if @Log_directories; } close UPDATE; $exit_status = $?; $total_records = scalar @Pathnames; $total_records = 250 # Maximum number of updates that can be reported. if ($total_records > 250); if ($exit_status == -1) { print STDERR "$Command_Name: The command could not be executed!\n"; print STDERR "\n@UPDATE_DB\n" unless $Verbose; exit ($UPDATE_FAILED); } elsif (($exit_status >>= 8) != $total_records) { print STDERR "$Command_Name: Update failed - exit status $exit_status.\n"; print STDERR "\n@UPDATE_DB\n" unless $Verbose; exit ($UPDATE_FAILED); } exit ($SUCCESS); #------------------------------------------------------------------------------- sub Accept_Source { my ($source) = @_; return 1 if (! $Unique); $Query_Sources = "\"" . "SELECT $PATHNAME_FIELD " . "FROM $Catalog.$Pipeline"."_Sources " . "WHERE $PATHNAME_FIELD='$source'"; $Query_Sources .= " AND $CONDUCTOR_ID_FIELD is NULL" if $Processed; $Query_Sources .= "\""; my $Query = "@QUERY_DB -Query $Query_Sources"; @result = qx/$Query/; if ($exit_status == -1) { print STDERR "$Command_Name: The command could not be executed!\n"; print STDERR "\n$Query\n" unless $Verbose; exit ($QUERY_FAILED); } elsif (($exit_status >>= 8) != 0) { print STDERR "$Command_Name: Query failed - exit status $exit_status.\n"; print STDERR "\n$Query\n" unless $Verbose; exit ($QUERY_FAILED); } foreach $line (@result) { # Look for a data record. return 0 if ($line =~ /^\d+ - /); } # No data record found. return 1; } #------------------------------------------------------------------------------- sub catalog_name { my ($name) = @_; return (($index = index ($name, '.')) > 0) ? substr ($name, 0, $index) : undef; } sub pipeline_name { my ($name) = @_; return (($index = index ($name, '.')) >= 0 && $index < (length ($name) - 1)) ? substr ($name, $index + 1) : undef; } #------------------------------------------------------------------------------- # Ensure that all directories of a path exist or are created. # sub Make_Path { my ($path) = @_; return 1 if (-d $path); my @segments = split (/\//, $path); my $path_to, $delimiter; $delimiter = '/' if (index ($path, '/') == 0); for my $segment (@segments) { next unless $segment; $path_to .= $delimiter . $segment; # Only the first segment may be without a delimiter. $delimiter = '/'; next if (-d $path_to); # The directory pathname segment already exists. if (-f $path_to) { print STDERR "A directory is needed where the $path_to file exists.\n"; return 0; } # Try to make the directory pathname segment. if (! mkdir $path_to) { # Making the directory failed. $retries = 5; while ($retries--) { # Wait up to 5 seconds and try again. sleep (int (rand (4)) + 1); last if (-d $path_to || mkdir $path_to); } if (! -d $path_to) { print STDERR "Unable to make the $path_to directory.\n", "$ERRNO\n"; return 0; } } } return 1; } pirl-2.3.8/PIRL/Conductor/Native_Methods.c0000644000175000017500000000237511742733132020141 0ustar mathieumathieu/* Native_Methods PIRL CVS ID: Native_Methods.c,v 1.5 2012/04/16 06:04:10 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ /* Implementation of JNI methods used by the Native_Methods class. */ #include "jni.h" JNIEXPORT jint JNICALL Java_PIRL_Conductor_Native_1Methods_get_1PID ( JNIEnv* java_environment, jclass java_class ) {return (jint)getpid ();} pirl-2.3.8/PIRL/Conductor/Native_Methods.java0000644000175000017500000000727611742733132020645 0ustar mathieumathieu/* Native_Methods PIRL CVS ID: Native_Methods.java,v 1.5 2012/04/16 06:04:10 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; /** Native methods.

An encapsulation of native method interfaces.

This class contains only static methods. It loads the required native library automatically the first time it is used.

@author Andrew Davidson, Bradford Castalia - UA/PIRL @version 1.5 */ public class Native_Methods { /** Class name and version identification. */ public static final String ID = "PIRL.Conductor.Native_Methods (1.5 2012/04/16 06:04:10)"; /** The name of the library that contains the native methods implementations. */ public static final String NATIVE_METHODS_LIBRARY_NAME = "Native_Methods"; /** Process ID of the JVM. */ private static int JVM_PID = 0; // Load the library and intialize data members. static {load_library ();} // Debug control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /** This class contains only static methods; there's nothing to construct. */ private Native_Methods () {} /** Load the {@link #NATIVE_METHODS_LIBRARY_NAME} library. After the library is successfully loaded the process ID of the JVM is cached locally. If the library fails to load successfully nothing is done. */ private static void load_library () { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Native_Methods.load_library"); try { System.loadLibrary (NATIVE_METHODS_LIBRARY_NAME); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Library " + NATIVE_METHODS_LIBRARY_NAME + " loaded."); JVM_PID = get_PID (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" JVM_PID initialized to " + JVM_PID); } catch (SecurityException exception) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" " + exception); } catch (UnsatisfiedLinkError exception) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" " + exception); } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Native_Methods.load_library"); } /** Native_Methods function interface. @return The PID obtained from the getpid system function. */ private static native int get_PID (); /** Get the process ID of the Java Virtual Machine. The process ID has been pre-fetched, so there is no overhead cost in repeated accesses to its value. If the library implementing the native method that provides the interface to the getpid system function did not load successfully, then the return value will be zero. @return The process ID (PID) of the JVM running this class. This will be zero if the native getpid system function could not be accessed because the {@link #NATIVE_METHODS_LIBRARY_NAME} library failed to load. */ public static int PID () {return JVM_PID;} } pirl-2.3.8/PIRL/Conductor/Processing_Changes.java0000644000175000017500000002574511742733132021501 0ustar mathieumathieu/* Processing_Changes PIRL CVS ID: Processing_Changes.java,v 1.13 2012/04/16 06:04:10 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Conductor; import PIRL.PVL.Parameter; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import java.util.Vector; /** Processing_Changes contains information provided by a Conductor about changes in its processing conditions.

@author Bradford Castalia - UA/PIRL @version 1.13 @see Conductor @see Processing_Event */ public class Processing_Changes { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.Processing_Changes ($Revison$ 2012/04/16 06:04:10)"; private Configuration Configuration = null; private int Processing_State = 0; private Vector Source_Record = null; private Vector Procedure_Record = null; private int Sequential_Failures = -1; private String Error_Condition = null; private boolean Sources_Refreshed = false, Procedures_Changed = false, Exiting = false; /** The value to set for all {@link #Configuration(Configuration) configuration} parameters named "Password" (case insensitive). */ public static final String MASKED_PASSWORD = "****"; private static final String NL = System.getProperty ("line.separator"); /*============================================================================== Constructors */ /** Construct Processing_Changes containing no changes. */ public Processing_Changes () {} /*============================================================================== Accessors */ /** Get the changed Configuration.

@return A copy of the changed Configuration. This will be null if the Configuration is unchanged. @see #Configuration(Configuration) */ public Configuration Configuration () {return Configuration;} /** Set the changed Configuration.

If the Configuration is non-null it is copied into this Processing_Changes. The copy is set to be {@link Configuration#Case_Sensitive(boolean) case insensitive} and all parameters named "PASSWORD" (case insensitive) have their values reset to {@link #MASKED_PASSWORD}.

@param configuration A Configuration. May be null. @return This Processing_Changes. @see #Configuration(Parameter) @see #Configuration() */ public Processing_Changes Configuration ( Configuration configuration ) throws Configuration_Exception { if (configuration == null) Configuration = null; else { Configuration = new Configuration (configuration); Configuration.Case_Sensitive (false); Configuration.Set_All ("PASSWORD", MASKED_PASSWORD); } return this; } /** Set the changed Configuration from a Parameter.

If the Parameter is non-null it is copied into the Configuration of this Processing_Changes. The copy is set to be {@link Configuration#Case_Sensitive(boolean) case insensitive} and all parameters named "PASSWORD" (case insensitive) have their values reset to {@link #MASKED_PASSWORD}.

@param parameters A Parameter. May be null. @return This Processing_Changes. @see #Configuration(Configuration) @see #Configuration() */ public Processing_Changes Configuration ( Parameter parameters ) throws Configuration_Exception { if (parameters == null) Configuration = null; else { Configuration = new Configuration (parameters); Configuration.Case_Sensitive (false); Configuration.Set_All ("PASSWORD", MASKED_PASSWORD); } return this; } /** Get the processing state.

The expected Conductor processing state values are:

1 - {@link Conductor#RUNNING}
Source records are being processing.
2 - {@link Conductor#POLLING}
No source records are currently available for processing; the Conductor is polling for new source records to process.
-1 - {@link Conductor#WAITING}
The Conductor is waiting to be told to being processing.
-2 - {@link Conductor#HALTED}
A problem condition caused the Conductor to stop processing. The problem may be the result of the maximum number of sequential source records processing procedure failures having occured, a database access failure, or some other system error.
0 - Unchanged
The processing state has not changed.

@return A Conductor processing state value, or zero the state is unchanged. @see #Processing_State(int) */ public int Processing_State () {return Processing_State;} /** Set the processing state.

@param processing_state A Conductor processing state value, or zero the state is unchanged. @return This Processing_Changes. @see #Processing_State() */ public Processing_Changes Processing_State ( int processing_state ) {Processing_State = processing_state; return this;} /** Get the changed source record.

@return A Vector containing the changed source record, or null if the record is unchanged. @see #Source_Record(Vector) */ public Vector Source_Record () {return Source_Record;} /** Set the changed source record.

If the record is non-null it is copied into this Processing_Changes.

@param source_record A Vector containing the changed source record, or null if the record is unchanged. @return This Processing_Changes. @see #Source_Record() */ public Processing_Changes Source_Record ( Vector source_record ) { if (source_record == null) Source_Record = null; else Source_Record = new Vector (source_record); return this; } /** Test if the source records cache was refreshed with new records.

@return true if the source records cache was refreshed with new records; false if the cache is unchanged. @see #Sources_Refreshed(boolean) */ public boolean Sources_Refreshed () {return Sources_Refreshed;} /** Set if the source records cache was refreshed with new records.

@param refreshed true if the source records cache was refreshed with new records; false if the cache is unchanged. @return This Processing_Changes. @see #Sources_Refreshed() */ public Processing_Changes Sources_Refreshed ( boolean refreshed ) {Sources_Refreshed = refreshed; return this;} /** Get the changed procedure record.

@return A Vector containing the changed procedure record, or null if the record is unchanged. @see #Procedure_Record(Vector) */ public Vector Procedure_Record () {return Procedure_Record;} /** Set the changed prcedure record.

If the record is non-null it is copied into this Processing_Changes.

@param procedure_record A Vector containing the changed procedure record, or null if the record is unchanged. @return This Processing_Changes. @see #Procedure_Record() */ public Processing_Changes Procedure_Record ( Vector procedure_record ) { if (procedure_record == null) Procedure_Record = null; else Procedure_Record = new Vector (procedure_record); return this; } /** Test if the procedure records have changed.

@return true if the procedure records were replaced with new records; false if the records are unchanged. @see #Procedures_Changed(boolean) */ public boolean Procedures_Changed () {return Procedures_Changed;} /** Set if the procedure records have changed.

@param changed true if the procedure records were replaced with new records; false if the records are unchanged. @return This Processing_Changes. @see #Procedures_Changed() */ public Processing_Changes Procedures_Changed ( boolean changed ) {Procedures_Changed = changed; return this;} /** Get the changed sequential failures limit.

@return The sequential failures limit. This will be -1 if the limit is unchanged. @see #Sequential_Failures(int) */ public int Sequential_Failures () {return Sequential_Failures;} /** Set the changed sequential failures limit.

@return The sequential failures limit. A negative value means the limit is unchanged. @return This Processing_Changes. @see #Sequential_Failures() */ public Processing_Changes Sequential_Failures ( int failure_count ) { if (failure_count >= 0) Sequential_Failures = failure_count; return this; } /** Get an error condition report.

@return A String describing the error condition. This will be null if no error condition occurred. @see #Error_Condition(String) */ public String Error_Condition () {return Error_Condition;} /** Set an error condition report.

@return A String describing the error condition. If null no error condition occurred. @return This Processing_Changes. @see #Error_Condition() */ public Processing_Changes Error_Condition ( String error_condition ) {Error_Condition = error_condition; return this;} /** Test if the Conductor is exiting.

@return true if the Conductor is exiting; false if the Conductor is still running. @see #Exiting(boolean) */ public boolean Exiting () {return Exiting;} /** Set if the Conductor is exiting.

@return true if the Conductor is exiting; false if the Conductor is still running. @return This Processing_Changes. @see #Exiting() */ public Processing_Changes Exiting ( boolean state ) {Exiting = state; return this;} /** Get a description of this Processing_Changes state.

The Processing_Changes object is described by its {@link #ID} followed by the name and values of each state variable.

The {@link #Configuration() Configuration} {@link Configuration#Description() description} will occupy many lines and may include at the end warnings from the PVL Lister about illegal syntax in values; this is to be expected and is harmless. If the Configuration is malformed its description will begin with an empty line followed by a description of the problem.

@return A multi-line String describing the state of this Processing_Changes object. */ public String toString () { return ID + NL +" Configuration -" + NL + ((Configuration == null) ? "null" : Configuration.Description ()) + NL +" Processing_State: " + Processing_State + NL +" Sources_Refreshed: " + Sources_Refreshed + NL +" Source_Record: " + Source_Record + NL +" Procedures_Changed: " + Procedures_Changed + NL +" Procedure_Record: " + Procedure_Record + NL +" Sequential_Failures: " + Sequential_Failures + NL +" Error_Condition -" + NL + Error_Condition + NL +" Exiting: " + Exiting + NL; } } pirl-2.3.8/PIRL/TreeTable/0000755000175000017500000000000012052546516014767 5ustar mathieumathieupirl-2.3.8/PIRL/TreeTable/TreeTableModelAdapter.java0000644000175000017500000001143011742152427021761 0ustar mathieumathieu/* TreeTableModelAdapter Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility. @(#)TreeTableModelAdapter.java 1.2 98/10/27 PIRL CVS ID: TreeTableModelAdapter.java,v 1.2 2012/04/14 01:38:31 castalia Exp */ package PIRL.TreeTable; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.tree.TreePath; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; /** This wrapper class takes a TreeTableModel and implements the table model interface. The implementation is trivial, with all of the event dispatching support provided by the superclass: the AbstractTableModel. @version 1.2 10/27/98 @author Philip Milne @author Scott Violet */ public class TreeTableModelAdapter extends AbstractTableModel { JTree tree; TreeTableModel treeTableModel; public TreeTableModelAdapter ( TreeTableModel treeTableModel, JTree tree ) { this.tree = tree; this.treeTableModel = treeTableModel; tree.addTreeExpansionListener (new TreeExpansionListener () { /* Don't use fireTableRowsInserted() here; the selection model would get updated twice. */ public void treeExpanded (TreeExpansionEvent event) {fireTableDataChanged ();} public void treeCollapsed (TreeExpansionEvent event) {fireTableDataChanged ();} }); /* Install a TreeModelListener that can update the table when tree changes. We use delayedFireTableDataChanged as we can not be guaranteed the tree will have finished processing the event before us. */ treeTableModel.addTreeModelListener (new TreeModelListener () { public void treeNodesChanged (TreeModelEvent e) {delayedFireTableDataChanged ();} public void treeNodesInserted (TreeModelEvent e) {delayedFireTableDataChanged ();} public void treeNodesRemoved (TreeModelEvent e) {delayedFireTableDataChanged ();} public void treeStructureChanged (TreeModelEvent e) {delayedFireTableDataChanged ();} }); } // Wrappers, implementing TableModel interface. public int getColumnCount () {return treeTableModel.getColumnCount ();} public String getColumnName (int column) {return treeTableModel.getColumnName (column);} public Class getColumnClass (int column) {return treeTableModel.getColumnClass (column);} public int getRowCount () {return tree.getRowCount ();} protected Object nodeForRow (int row) { TreePath treePath = tree.getPathForRow (row); return treePath.getLastPathComponent (); } public Object getValueAt (int row, int column) {return treeTableModel.getValueAt (nodeForRow (row), column);} public boolean isCellEditable (int row, int column) {return treeTableModel.isCellEditable (nodeForRow (row), column);} public void setValueAt (Object value, int row, int column) {treeTableModel.setValueAt (value, nodeForRow( row), column);} /* Invokes fireTableDataChanged after all the pending events have been processed. SwingUtilities.invokeLater is used to handle this. */ protected void delayedFireTableDataChanged () { SwingUtilities.invokeLater (new Runnable () { public void run () {fireTableDataChanged ();} }); } } // class TreeTableModelAdapter. pirl-2.3.8/PIRL/TreeTable/TreeTableModel.java0000644000175000017500000000613111742152427020462 0ustar mathieumathieu/* TreeTableModel.java Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility. PIRL CVS ID: TreeTableModel.java,v 1.2 2012/04/14 01:38:31 castalia Exp */ package PIRL.TreeTable; import javax.swing.tree.TreeModel; /** TreeTableModel is the model used by a JTreeTable. It extends TreeModel to add methods for getting inforamtion about the set of columns each node in the TreeTableModel may have. Each column, like a column in a TableModel, has a name and a type associated with it. Each node in the TreeTableModel can return a value for each of the columns and set that value if isCellEditable() returns true. @author Philip Milne @author Scott Violet */ public interface TreeTableModel extends TreeModel { /** Returns the number ofs availible column. */ public int getColumnCount (); /** Returns the name for column number column. */ public String getColumnName (int column); /** Returns the type for column number column. */ public Class getColumnClass (int column); /** Returns the value to be displayed for node node, at column number column. */ public Object getValueAt (Object node, int column); /** Indicates whether the the value for node node, at column number column is editable. */ public boolean isCellEditable (Object node, int column); /** Sets the value for node node, at column number column. */ public void setValueAt (Object aValue, Object node, int column); } pirl-2.3.8/PIRL/TreeTable/package.html0000644000175000017500000000113607424743173017256 0ustar mathieumathieu TreeTable is a packaged version of Sun's TreeTable example files. As example code, the documentation is scanty and not intended to provide a thorugh description of its uses. The tutorial article - "Creating TreeTables" - describes the use of the classes for a file hierarchy display application. The classes here have been stripped down for uses with other applications. pirl-2.3.8/PIRL/TreeTable/AbstractCellEditor.java0000644000175000017500000000413111742152375021344 0ustar mathieumathieu/* AbstractCellEditor PIRL CVS ID: AbstractCellEditor.java,v 1.2 2012/04/14 01:38:05 castalia Exp */ package PIRL.TreeTable; import java.awt.Component; import java.awt.event.*; import java.awt.AWTEvent; import javax.swing.*; import javax.swing.event.*; import java.util.EventObject; import java.io.Serializable; public class AbstractCellEditor implements CellEditor { protected EventListenerList listenerList = new EventListenerList (); public Object getCellEditorValue () {return null;} public boolean isCellEditable ( EventObject event ) {return true;} public boolean shouldSelectCell ( EventObject event ) {return false;} public boolean stopCellEditing () {return true;} public void cancelCellEditing () {} public void addCellEditorListener ( CellEditorListener listener ) {listenerList.add (CellEditorListener.class, listener);} public void removeCellEditorListener ( CellEditorListener listener ) {listenerList.remove (CellEditorListener.class, listener);} /* Notify all listeners that have registered interest for notification on this event type. */ protected void fireEditingStopped () { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length - 2; listener >= 0; listener -= 2) if (listeners[listener] == CellEditorListener.class) ((CellEditorListener)listeners[listener + 1]) .editingStopped (new ChangeEvent (this)); } /* Notify all listeners that have registered interest for notification on this event type. */ protected void fireEditingCanceled () { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length - 2; listener >= 0; listener -= 2) if (listeners[listener] == CellEditorListener.class) ((CellEditorListener)listeners[listener + 1]) .editingCanceled (new ChangeEvent (this)); } } // class AbstractCellEditor. pirl-2.3.8/PIRL/TreeTable/Makefile0000644000175000017500000000116407757777144016454 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.3 2003/11/23 00:25:08 castalia Exp # gmake syntax JPATH ?= ../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = AbstractTreeTableModel.class \ JTreeTable.class \ TreeTableModel.class \ TreeTableModelAdapter.class \ AbstractCellEditor.class \ all: classes classes: ${CLASSES} clean: rm -f *.class AbstractTreeTableModel.class: TreeTableModel.class TreeTableModelAdapter.class: TreeTableModel.class JTreeTable.class: TreeTableModelAdapter.class \ AbstractCellEditor.class pirl-2.3.8/PIRL/TreeTable/JTreeTable.java0000644000175000017500000005506110612044464017615 0ustar mathieumathieu/* JTreeTable Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility. Modfied at: Planetary Image Research Laboratory University of Arizona Tucson, AZ 85721-0092 By: Bradford Castalia PIRL CVS ID: JTreeTable.java,v 1.4 2007/04/20 05:01:40 castalia Exp */ package PIRL.TreeTable; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.tree.*; import javax.swing.table.*; import java.awt.Dimension; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.InputEvent; import java.util.EventObject; /** * This example shows how to create a simple JTreeTable component, * by using a JTree as a renderer (and editor) for the cells in a * particular column in the JTable. * * @version 1.2 10/27/98 * * @author Philip Milne * @author Scott Violet */ public class JTreeTable extends JTable { /** A subclass of JTree. */ protected TreeTableCellRenderer The_Tree; private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_MANIPULATORS = 1 << 2, DEBUG_CELL_EDITOR = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; public JTreeTable ( TreeTableModel treeTableModel ) { super(); // Create the tree. It will be used as a renderer and editor. The_Tree = new TreeTableCellRenderer (treeTableModel); // Install a tableModel representing the visible rows in the tree. super.setModel (new TreeTableModelAdapter (treeTableModel, The_Tree)); // Force the JTable and JTree to share their row selection models. ListToTreeSelectionModelWrapper selectionWrapper = new ListToTreeSelectionModelWrapper (); The_Tree.setSelectionModel (selectionWrapper); setSelectionModel (selectionWrapper.getListSelectionModel ()); // Install the tree editor renderer and editor. setDefaultRenderer (TreeTableModel.class, The_Tree); setDefaultEditor (TreeTableModel.class, new TreeTableCellEditor ()); // No grid. setShowGrid (false); // No intercell spacing setIntercellSpacing (new Dimension (0, 0)); if (The_Tree.getRowHeight () < 1) // Metal looks better like this. setRowHeight (18); } /** Gets the Object at a row of the Tree. */ public Object getTreeNode ( int row ) {return ((TreeTableModelAdapter)getModel ()).nodeForRow (row);} /** Overridden to message super and forward the method to the tree. Since the tree is not actually in the component hierarchy it will never receive this unless we forward it in this manner. */ public void updateUI () { super.updateUI (); if (The_Tree != null) { The_Tree.updateUI (); /* Do this so that the editor is referencing the current renderer from the tree. The renderer can potentially change each time laf changes. */ setDefaultEditor (TreeTableModel.class, new TreeTableCellEditor ()); } // Use the tree's default foreground and background colors in the // table. LookAndFeel.installColorsAndFont ( this, "Tree.background", "Tree.foreground", "Tree.font" ); } /** Workaround for BasicTableUI anomaly. Make sure the UI never tries to paint the editor. The UI currently uses different techniques to paint the renderers and editors and overriding setBounds() below is not the right thing to do for an editor. Returning -1 for the editing row in this case, ensures the editor is never painted. */ public int getEditingRow () { return (getColumnClass (editingColumn) == TreeTableModel.class) ? -1 : editingRow; } /** Gets the actual row that is editing as getEditingRow will always return -1. */ private int realEditingRow () {return editingRow;} /** This is overridden to invoke super's implementation, and then, if the receiver is editing a Tree column, the editor's bounds is reset. The reason we have to do this is because JTable doesn't think the table is being edited, as getEditingRow returns -1, and therefore doesn't automatically resize the editor for us. */ public void sizeColumnsToFit ( int resizingColumn ) { super.sizeColumnsToFit (resizingColumn); if (getEditingColumn () != -1 && getColumnClass (editingColumn) == TreeTableModel.class) { Rectangle cellRect = getCellRect (realEditingRow (), getEditingColumn (), false); Component component = getEditorComponent (); component.setBounds (cellRect); component.validate(); } } /** Overridden to pass the new rowHeight to the tree. */ public void setRowHeight ( int rowHeight ) { super.setRowHeight (rowHeight); if (The_Tree != null && The_Tree.getRowHeight () != rowHeight) The_Tree.setRowHeight (getRowHeight ()); } /** Overridden to invoke repaint for the particular location if the column contains the tree. This is done as the tree editor does not fill the bounds of the cell, we need the renderer to paint the tree in the background, and then draw the editor over it. */ public boolean editCellAt ( int row, int column, EventObject event ) { boolean result = super.editCellAt (row, column, event); if (result && getColumnClass (column) == TreeTableModel.class) repaint (getCellRect (row, column, false)); return result; } /** Get the tree that is being shared between the model. */ public JTree getTree () {return The_Tree;} /*------------------------------------------------------------------------------ */ /** A TreeCellRenderer that displays a JTree. */ public class TreeTableCellRenderer extends JTree implements TableCellRenderer { /** Last table/tree row asked to render. */ protected int visibleRow; /** Border to draw around the tree. If this is non-null, it will be painted. */ protected Border highlightBorder; public TreeTableCellRenderer ( TreeModel model ) {super (model);} /** Set the colors of the Tree's renderer to match that of the table. */ public void updateUI () { if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">>> JTreeTable.updateUI"); super.updateUI (); // Make the tree's cell renderer use the table's cell selection colors. TreeCellRenderer renderer = getCellRenderer (); if (renderer instanceof DefaultTreeCellRenderer) { DefaultTreeCellRenderer default_renderer = ((DefaultTreeCellRenderer)renderer); /* For 1.1 uncomment this, 1.2 has a bug that will cause an exception to be thrown if the border selection color is null. */ // default_renderer.setBorderSelectionColor (null); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" UIManager.getColor (\"Table.selectionForeground\") = " + UIManager.getColor ("Table.selectionForeground") + '\n' +" UIManager.getColor (\"Table.selectionBackground\") = " + UIManager.getColor ("Table.selectionBackground")); default_renderer.setTextSelectionColor (UIManager.getColor ("Table.selectionForeground")); default_renderer.setBackgroundSelectionColor (UIManager.getColor ("Table.selectionBackground")); } if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println ("<<< JTreeTable.updateUI"); } /** Sets the row height of the tree, and forward the row height to the table. */ public void setRowHeight ( int rowHeight ) { if (rowHeight > 0) { super.setRowHeight (rowHeight); if (JTreeTable.this != null && JTreeTable.this.getRowHeight () != rowHeight) JTreeTable.this.setRowHeight (getRowHeight ()); } } /** Set the height to match that of the JTable. */ public void setBounds ( int x, int y, int w, int h ) {super.setBounds (x, 0, w, JTreeTable.this.getHeight ());} /** Translate the graphics such that the last visible row will be drawn at 0,0. */ public void paint ( Graphics graphics ) { if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">>> JTreeTable.paint"); graphics.translate (0, -visibleRow * getRowHeight ()); super.paint (graphics); // Draw the Table border if we have focus. if (highlightBorder != null) highlightBorder.paintBorder (this, graphics, 0, visibleRow * getRowHeight (), getWidth (), getRowHeight ()); // Fill in a cell that has an oversized row height. if (JTreeTable.this.getRowHeight (visibleRow) > getRowHeight ()) { if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" Visible row " + visibleRow + " height = " + JTreeTable.this.getRowHeight (visibleRow) + " > normal row height " + getRowHeight () + '\n' +" Width = " + getWidth () + '\n' +" graphics.setColor (UIManager.getColor" + " (\"Table.selectionBackground\") = " + UIManager.getColor ("Table.selectionBackground") + '\n' +" graphics.fillRect (0, " + visibleRow * getRowHeight() + getRowHeight () + ", " + getWidth () + ", " + JTreeTable.this.getRowHeight (visibleRow) +')'); graphics.setColor (UIManager.getColor ("Table.selectionBackground")); graphics.fillRect (0, visibleRow * getRowHeight() + getRowHeight (), getWidth (), JTreeTable.this.getRowHeight (visibleRow)); } if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println ("<<< JTreeTable.paint"); } /** Update the visible row. */ public Component getTableCellRendererComponent ( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column ) { if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">>> JTreeTable.getTableCellRendererComponent:" + " row " + row + ", column " + column); Color background, foreground; if (isSelected) { background = table.getSelectionBackground (); foreground = table.getSelectionForeground (); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" isSelected true\n" +" table.getSelectionBackground () = " + background + '\n' +" table.getSelectionForeground () = " + foreground); } else { background = table.getBackground (); foreground = table.getForeground (); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" isSelected false\n" +" table.getBackground () = " + background + '\n' +" table.getForeground () = " + foreground); } highlightBorder = null; if (realEditingRow () == row && getEditingColumn () == column) { background = UIManager.getColor ("Table.focusCellBackground"); foreground = UIManager.getColor ("Table.focusCellForeground"); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" Editing this row and column\n" +" UIManager.getColor (\"Table.focusCellBackground\") = " + background + '\n' +" UIManager.getColor (\"Table.focusCellForeground\") = " + foreground); } else if (hasFocus) { highlightBorder = UIManager.getBorder ("Table.focusCellHighlightBorder"); if (isCellEditable (row, column)) { background = UIManager.getColor ("Table.focusCellBackground"); foreground = UIManager.getColor ("Table.focusCellForeground"); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" hasFocus true and cell is editable\n" +" UIManager.getColor (\"Table.focusCellBackground\") = " + background + '\n' +" UIManager.getColor (\"Table.focusCellForeground\") = " + foreground); } } visibleRow = row; setBackground (background); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" Visible row: " + row + '\n' +" Background = " + background); TreeCellRenderer renderer = getCellRenderer (); if (renderer instanceof DefaultTreeCellRenderer) { DefaultTreeCellRenderer default_renderer = ((DefaultTreeCellRenderer)renderer); if (isSelected) { default_renderer.setTextSelectionColor (foreground); default_renderer.setBackgroundSelectionColor (background); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" default_renderer.setBackgroundSelectionColor = " + background + '\n' +" default_renderer.setTextSelectionColor = " + foreground); } else { default_renderer.setTextNonSelectionColor (foreground); default_renderer.setBackgroundNonSelectionColor (background); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (" default_renderer.setBackgroundNonSelectionColor = " + background + '\n' +" default_renderer.setTextNonSelectionColor = " + foreground); } } if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println ("<<< JTreeTable.getTableCellRendererComponent"); return this; } } // class TreeTableCellRenderer. /*------------------------------------------------------------------------------ */ /** TreeTableCellEditor implementation. An editor that can be used to edit the tree column. This extends DefaultCellEditor and uses a JTextField (actually, TreeTableTextField) to perform the actual editing.

To support editing of the tree column we can not make the tree editable. The reason this doesn't work is that you can not use the same component for editing and renderering. The table may have the need to paint cells, while a cell is being edited. If the same component were used for the rendering and editing the component would be moved around, and the contents would change. When editing, this is undesirable, the contents of the text field must stay the same, including the caret blinking, and selections persisting. For this reason the editing is done via a TableCellEditor.

Another interesting thing to be aware of is how tree positions its render and editor. The render/editor is responsible for drawing the icon indicating the type of node (leaf, branch...). The tree is responsible for drawing any other indicators, perhaps an additional +/- sign, or lines connecting the various nodes. So, the renderer is positioned based on depth. On the other hand, table always makes its editor fill the contents of the cell. To get the allusion that the table cell editor is part of the tree, we don't want the table cell editor to fill the cell bounds. We want it to be placed in the same manner as tree places it editor, and have table message the tree to paint any decorations the tree wants. Then, we would only have to worry about the editing part. The approach taken here is to determine where tree would place the editor, and to override the reshape method in the JTextField component to nudge the textfield to the location tree would place it. Since JTreeTable will paint the tree behind the editor everything should just work. So, that is what we are doing here. Determining of the icon position will only work if the TreeCellRenderer is an instance of DefaultTreeCellRenderer. If you need custom TreeCellRenderers, that don't descend from DefaultTreeCellRenderer, and you want to support editing in JTreeTable, you will have to do something similiar. */ public class TreeTableCellEditor extends DefaultCellEditor { public TreeTableCellEditor () {super (new TreeTableTextField ());} /** Overridden to determine an offset where the tree would place the editor. The offset is determined from the getRowBounds JTree method, and additionally from the icon DefaultTreeCellRenderer will use. The offset is then set on the TreeTableTextField component created in the constructor, and returned. */ public Component getTableCellEditorComponent ( JTable table, Object value, boolean isSelected, int row, int column ) { if ((DEBUG & DEBUG_CELL_EDITOR) != 0) System.out.println (">>> JTreeTable.TreeTableCellEditor.getTableCellEditorComponent:\n" +" isSelected " + isSelected + ", row " + row + ", column " + column); Component component = super.getTableCellEditorComponent (table, value, isSelected, row, column); JTree tree = getTree (); int offsetRow = tree.isRootVisible () ? row : row - 1; if (offsetRow < 0) offsetRow = 0; Rectangle bounds = tree.getRowBounds (offsetRow); if ((DEBUG & DEBUG_CELL_EDITOR) != 0) System.out.println (" bounds at offsetRow " + offsetRow + " = " + bounds); int offset = bounds.x; TreeCellRenderer renderer = tree.getCellRenderer (); if (renderer instanceof DefaultTreeCellRenderer) { Object node = tree.getPathForRow (offsetRow).getLastPathComponent (); Icon icon; if (tree.getModel ().isLeaf (node)) icon = ((DefaultTreeCellRenderer)renderer).getLeafIcon (); else if (The_Tree.isExpanded (offsetRow)) icon = ((DefaultTreeCellRenderer)renderer).getOpenIcon (); else icon = ((DefaultTreeCellRenderer)renderer).getClosedIcon (); if (icon != null) offset += ((DefaultTreeCellRenderer)renderer).getIconTextGap () + icon.getIconWidth (); } ((TreeTableTextField)getComponent ()).offset = offset; if ((DEBUG & DEBUG_CELL_EDITOR) != 0) System.out.println ("<<< JTreeTable.TreeTableCellEditor.getTableCellEditorComponent:"); return component; } /** Overridden to forward the event to the tree. Returns true if the click count >= 3, or the event is null. */ public boolean isCellEditable ( EventObject event ) { if (event instanceof MouseEvent) { MouseEvent mouse_event = (MouseEvent)event; /* If the modifiers are not 0 (or the left mouse button), tree may try and toggle the selection, and table will then try and toggle, resulting in the selection remaining the same. To avoid this, we only dispatch when the modifiers are 0 (or the left mouse button). */ if (mouse_event.getModifiers () == 0 || mouse_event.getModifiers() == InputEvent.BUTTON1_MASK) { for (int counter = getColumnCount () - 1; counter >= 0; counter--) { if (getColumnClass (counter) == TreeTableModel.class) { JTreeTable.this.The_Tree.dispatchEvent (new MouseEvent ( JTreeTable.this.The_Tree, mouse_event.getID (), mouse_event.getWhen (), mouse_event.getModifiers (), mouse_event.getX () - getCellRect (0, counter, true).x, mouse_event.getY (), mouse_event.getClickCount (), mouse_event.isPopupTrigger () )); break; } } } if (mouse_event.getClickCount () >= 3) return true; return false; } if (event == null) return true; return false; } } // class TreeTableCellEditor. /*------------------------------------------------------------------------------ */ /** Component used by TreeTableCellEditor. The only thing this does is to override the reshape method, and to ALWAYS make the x location be offset. */ static class TreeTableTextField extends JTextField { public int offset; public void reshape ( int x, int y, int w, int h ) { int newX = Math.max(x, offset); super.reshape (newX, y, w - (newX - x), h); } } // class TreeTableTextField. /*------------------------------------------------------------------------------ */ /** ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel to listen for changes in the ListSelectionModel it maintains. Once a change in the ListSelectionModel happens, the paths are updated in the DefaultTreeSelectionModel. */ class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { /** Set to true when we are updating the ListSelectionModel. */ protected boolean updatingListSelectionModel; public ListToTreeSelectionModelWrapper () { super (); getListSelectionModel () .addListSelectionListener (createListSelectionListener ()); } /** Returns the list selection model. ListToTreeSelectionModelWrapper listens for changes to this model and updates the selected paths accordingly. */ ListSelectionModel getListSelectionModel () {return listSelectionModel;} /** Sets updatingListSelectionModel and message super. This is the only place DefaultTreeSelectionModel alters the ListSelectionModel. */ public void resetRowSelection () { if (! updatingListSelectionModel) { updatingListSelectionModel = true; try {super.resetRowSelection ();} finally {updatingListSelectionModel = false;} } /* Notice how we don't message super if updatingListSelectionModel is true. If updatingListSelectionModel is true, it implies the ListSelectionModel has already been updated and the paths are the only thing that needs to be updated. */ } /** Creates and returns an instance of ListSelectionHandler. */ protected ListSelectionListener createListSelectionListener () {return new ListSelectionHandler ();} /** If updatingListSelectionModel is false, reset the selected paths from the selected rows in the list selection model. */ protected void updateSelectedPathsFromSelectedRows () { if (! updatingListSelectionModel) { updatingListSelectionModel = true; try { /* This is way expensive, ListSelectionModel needs an enumerator for iterating. */ int min = listSelectionModel.getMinSelectionIndex (), max = listSelectionModel.getMaxSelectionIndex (); clearSelection (); if (min != -1 && max != -1) { for (int counter = min; counter <= max; counter++) { if (listSelectionModel.isSelectedIndex (counter)) { TreePath path = The_Tree.getPathForRow (counter); if (path != null) addSelectionPath (path); } } } } finally {updatingListSelectionModel = false;} } } /*------------------------------------------------------------------------------ */ /** Class responsible for calling updateSelectedPathsFromSelectedRows when the selection of the list changes. */ class ListSelectionHandler implements ListSelectionListener { public void valueChanged ( ListSelectionEvent event ) {updateSelectedPathsFromSelectedRows ();} } // class ListSelectionHandler. } // class ListToTreeSelectionModelWrapper. } // class JTreeTable. pirl-2.3.8/PIRL/TreeTable/AbstractTreeTableModel.java0000644000175000017500000001747011742152427022156 0ustar mathieumathieu/* AbstractTreeTableModel Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistribution in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You acknowledge that this software is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility. @(#)AbstractTreeTableModel.java 1.2 98/10/27 PIRL CVS ID: AbstractTreeTableModel.java,v 1.2 2012/04/14 01:38:31 castalia Exp */ package PIRL.TreeTable; import javax.swing.tree.*; import javax.swing.event.*; /** * @version 1.2 10/27/98 * An abstract implementation of the TreeTableModel interface, handling the list * of listeners. * @author Philip Milne */ public abstract class AbstractTreeTableModel implements TreeTableModel { protected Object root; protected EventListenerList listenerList = new EventListenerList (); public AbstractTreeTableModel (Object root) {this.root = root;} /*============================================================================== Default implmentations for methods in the TreeModel interface. */ public Object getRoot () {return root;} public boolean isLeaf ( Object node ) {return getChildCount (node) == 0;} public void valueForPathChanged ( TreePath path, Object newValue ) {} // This is not called in the JTree's default mode: use a naive implementation. public int getIndexOfChild ( Object parent, Object child ) { for (int i = 0; i < getChildCount (parent); i++) if (getChild (parent, i).equals (child)) return i; return -1; } public void addTreeModelListener ( TreeModelListener listener ) {listenerList.add (TreeModelListener.class, listener);} public void removeTreeModelListener ( TreeModelListener listener ) {listenerList.remove (TreeModelListener.class, listener);} /** Notify all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method. @see EventListenerList */ protected void fireTreeNodesChanged ( Object source, Object[] path, int[] childIndices, Object[] children ) { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); TreeModelEvent event = null; /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length - 2; listener >= 0; listener -= 2) { if (listeners[listener] == TreeModelListener.class) { // Lazily create the event: if (event == null) event = new TreeModelEvent (source, path, childIndices, children); ((TreeModelListener)listeners[listener + 1]).treeNodesChanged (event); } } } /** Notify all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method. @see EventListenerList */ protected void fireTreeNodesInserted ( Object source, Object[] path, int[] childIndices, Object[] children ) { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); TreeModelEvent event = null; /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length-2; listener >= 0; listener -= 2) { if (listeners[listener] == TreeModelListener.class) { // Lazily create the event: if (event == null) event = new TreeModelEvent (source, path, childIndices, children); ((TreeModelListener)listeners[listener + 1]).treeNodesInserted (event); } } } /** Notify all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method. @see EventListenerList */ protected void fireTreeNodesRemoved ( Object source, Object[] path, int[] childIndices, Object[] children ) { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); TreeModelEvent event = null; /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length-2; listener >= 0; listener -= 2) { if (listeners[listener] == TreeModelListener.class) { // Lazily create the event: if (event == null) event = new TreeModelEvent (source, path, childIndices, children); ((TreeModelListener)listeners[listener + 1]).treeNodesRemoved (event); } } } /** Notify all listeners that have registered interest for notification on this event type. The event instance is lazily created using the parameters passed into the fire method. @see EventListenerList */ protected void fireTreeStructureChanged ( Object source, Object[] path, int[] childIndices, Object[] children ) { // Guaranteed to return a non-null array. Object[] listeners = listenerList.getListenerList (); TreeModelEvent event = null; /* Process the listeners last to first, notifying those that are interested in this event. */ for (int listener = listeners.length-2; listener >= 0; listener -= 2) { if (listeners[listener] == TreeModelListener.class) { // Lazily create the event: if (event == null) event = new TreeModelEvent (source, path, childIndices, children); ((TreeModelListener)listeners[listener + 1]).treeStructureChanged (event); } } } /*============================================================================== Default impelmentations for methods in the TreeTableModel interface. */ public Class getColumnClass ( int column ) {return Object.class;} /** By default, only the column with the Tree in it is editable. Making this column editable causes the JTable to forward mouse and keyboard events in the Tree column to the underlying JTree. */ public boolean isCellEditable ( Object node, int column ) {return getColumnClass (column) == TreeTableModel.class;} public void setValueAt ( Object aValue, Object node, int column ) {} /*============================================================================== Left to be implemented in the subclass: public Object getChild (Object parent, int index) public int getChildCount (Object parent) public int getColumnCount () public String getColumnName (Object node, int column) public Object getValueAt (Object node, int column) */ } // class AbstractTreeTableModel. pirl-2.3.8/PIRL/Messenger/0000755000175000017500000000000012052546521015044 5ustar mathieumathieupirl-2.3.8/PIRL/Messenger/package.html0000644000175000017500000001617711742734030017340 0ustar mathieumathieu The Messenger package provides an interprocess messaging service.

A Message contains routing information for the message content. The content is defined in terms of an Aggregate of {@link PIRL.PVL.Parameter Parameters} having names and {@link PIRL.PVL.Value Values} suitable for the protocol being employed. A set of basic parameter names and corresponding Message generators is provided for use by a typical protocol. In addition to the base Parameter class methods for manipulating the content, a set of methods to simplify Message routing and content management and access are provided. The content can be represented in transmittable standard PVL syntax packet form in a ByteBuffer. The packet will contain a label with the message routing information and message start synchronization pattern that provides for message stream unit recovery in an unreliable transmission medium. The routing information is route-from and route-to address String lists (Array Values) which may be used as desired by the transmission agent.

A Messenger is a transmission agent for Message objects. A Messenger provides Message transmission and synchronous or asynchronous Message receiving capabilities over a blocking SocketChannel. The SocketChannel may be provided when the Messenger is constructed or by the Messenger if an InetSocketAddress or hostname (or IP address) and network port number is provided. Messengers are expected (but not required) to work in pairs with the "client" side Messenger being constructed on a new SocketChannel connected to a SocketAddress representing the server side address (typically this is done by just constructing a Messenger using a hostname and port number), and the "server" side Messenger being constructed on a SocketChannel accepted from a network connection of a client to a ServerSocketChannel. A Messenger manages Message packetization and transmission on the SocketChannel and reading the SocketChannel for message packets (including synchronization and optional timeout) and Message reconstruction. Asynchronous Message receiving is done in a Message listener Thread, provided by the Messenger, that is started and stopped by the user. The Message listener Thread handles reading the Message stream - using the same mechanism employed by synchronous Message receipt - and routing.

Each Messenger has a unique address - composed of its host system IP address, port number and start time (milliseconds) - that is used in the Message routing address lists. When a Message is sent the address of the Messenger is added to the route-from list. When a Message packet is received the Messenger's address is removed from the end of the route-to list. During asynchronous packet receipt processing if another address remains on the route-to list and it matches the address of one of the forwarding Messengers registered with the Messenger the packet that was read is given to the forwarding Messenger to be sent on to the next Messenger (an incorrectly addressed Message is returned to sender). Otherwise the Message is depacketized and delivered to the Messenger Employer in a Message_Delivered_Event via its Message_Delivered_Listener interface. Each Messenger will be bound to an identity Message that will contain at least an "Address" parameter with the Messenger's address, but is typically supplemented by the Employer with other parameters that identify the Employer or perhaps the Employer of the client Messenger for a server side Messanger.

A Dispatcher is a server-side Employer of Messengers. A Dispatcher can be used as a stand-alone application or as a Thread Runnable by some other application. It can be provided with a Configuration or allowed to read a default configuration file based on a name assigned to the Dispatcher or its default name. When a Dispatcher is started it broadcasts its presence to the local host on a configured multicast address (230.1.2.3 by default) and port number (4170 by default) to let potential clients on the same host know that they can initiate their connections. It then binds to a ServerSocketChannel on a configured port (4144 by default) and listens for client connections. When a client connects a Messenger is provided for it. This Messenger is used to to send the client a Message requesting that it identify itself. The client must respond with an identification Message, which is then bound to the Messenger as its identity, or it will be disconnected. This exchange also includes (unless disabled) and authentication process in which the Dispatcher provides an encoded public key object which the client must use to encode a password (of any length) that is sent back to the Dispatcher that uses its private key to decode and compare against its configured password. If the authentication does not succeed the client is disconnected. The Dispatcher maintains a list of all authenticated Messengers and sends the identity Message for each Messenger on the list to all new clients as well as all existing clients that have requested notification of changes to the connected Messengers list. The Dispatcher implements a simple Message content protocol that recognizes Messages with several "Action" parameters. This includes requests for all or named connected Messenger identifications, or to be added or removed from the list of Messengers to receive automatic notification of changes to the connected Messengers list; to be direct communication linked or unlinked with another Messenger by having it added or removed from the forwarding Messenger registration, and vice versa for the other Messenger; to obtain a status report including the memory usage of the Dispatcher and transmissions statistics of each Messenger; and to indicate that it is done and is to be cleanly disconnected and removed from the connected Messengers list. A Dispatcher includes a facility for optionally logging all of its activities to a configured file.

A Messenger_Styled_Writer is included in the package that extends the standard Writer class with a {@link PIRL.Utilities.Styled_Writer} interface implemented with Messages sent via a specified Messenger. This enables a Writer stream to be multiplexed along with other protocol Messages over an established Messenger connection. pirl-2.3.8/PIRL/Messenger/Message_Delivered_Event.java0000644000175000017500000000324711742734030022424 0ustar mathieumathieu/* Message_Delivered_Event PIRL CVS ID: Message_Delivered_Event.java,v 1.5 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import java.util.EventObject; /** A Message_Delivered_Event is used to notify a Messenger employer that a message was delivered needing attention.

@author Bradford Castalia - UA/PIRL @version 1.5 @see Message @see Messenger */ public class Message_Delivered_Event extends EventObject { public Message Message; /** Constructs a Message_Delivered_Event.

@param messenger The Messenger that is delivering the message. @param message The Message being delivered. */ public Message_Delivered_Event ( Messenger messenger, Message message ) { super (messenger); Message = message; } } pirl-2.3.8/PIRL/Messenger/Messenger_Styled_Writer.java0000644000175000017500000006355511742734030022534 0ustar mathieumathieu/* Messenger_Styled_Writer PIRL CVS ID: Messenger_Styled_Writer.java,v 1.13 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import PIRL.Utilities.Styled_Writer; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.Strings.String_Buffer; import PIRL.PVL.PVL_Exception; import java.io.Writer; import java.io.IOException; import java.io.EOFException; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.TabSet; import javax.swing.text.TabStop; import java.awt.Color; import java.util.Hashtable; import java.util.Vector; import java.util.Iterator; import java.util.Enumeration; /** A Messenger_Styled_Writer is a Writer that encapsulates what is written, along with Styled_Writer information, into a Message that is sent via a Messenger.

Each write operation produces a Message that is sent to the Messenger to which a Messenger_Styled_Writer is bound. The Message contains a "Write" Action parameter and a "Written" parameter with a string value that is what was written. Styled_Writer information, if present, is contained in a "Style" Aggregate parameter in which each member describes style properties.

Messages are routed by the Messenger according to the route-to address list that may be provided. If no route-to list is provided Messages that are sent will be delivered to the receiving Messenger's Employer.

No buffering is done; each write operation causes a message to be immediately sent.

Message sending may be suspended. While a Messenger_Styled_Writer is suspended all write operations have no effect. Argument parameters will still be checked for validity, where applicable.

@author Bradford Castalia - UA/PIRL @version 1.13 @see Writer @see Messenger @see Message */ public class Messenger_Styled_Writer extends Writer implements Styled_Writer { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utilities.Messenger_Styled_Writer (1.13 2012/04/16 06:11:36)"; /** The {@link Message#ACTION_PARAMETER_NAME} value that identifies the Message sent to the Messenger as coming from a Messenger_Styled_Writer. */ public static final String WRITE_ACTION = "Write"; /** The name of the Message parameter whose value is what was written. */ public static final String WRITTEN_PARAMETER_NAME = "Written"; /** The name of the Parameter Group that contains the style parameters. */ public static final String STYLE_PARAMETER_GROUP = "Style"; /** The suspension state of the Writer. */ public volatile boolean Suspended = false; private Messenger The_Messenger; private Value Route; private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_WRITE = 1 << 2, DEBUG_SEND = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Messenger_Styled_Writer on a Messenger with a Message route address list.

N.B.: Unless the messenger {@link Messenger#Is_Connected() is connected} to its communication channel this Messenger_Styled_Writer will be initially {@link #Closed() closed} and thus useless.

@param messenger The Messenger to which Writer Messages will be sent. @param route An Array Value containing the route-to address list to be used when sending Messages. If null no message routing will be used. @throws IllegalArgumentException If the messenger is null or the route is not an Array of address Strings. */ public Messenger_Styled_Writer ( Messenger messenger, Value route ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Messenger_Styled_Writer"); if ((The_Messenger = messenger) == null) throw new IllegalArgumentException (ID + NL + "Can't construct with a null Messenger."); if (route == null) { try {Route = new Value ().Type (Value.SEQUENCE);} catch (PVL_Exception exception) {/* Can't happen */} } else { try {Route = new Value (Message.Valid_Route (route));} catch (PVL_Exception exception) { throw new IllegalArgumentException (ID + NL + "Can't construct with an invalid address route." + NL + exception.getMessage ()); } } } /** Construct a Messenger_Styled_Writer on a Messenger.

Unless the Messenger {@link Messenger#Is_Connected() is connected} to its communication channel this Messenger_Styled_Writer will be initially {@link #Closed() closed} and thus useless.

: No Message routing will be used; Messages sent will be delivered to the receiving Messenger's Employer rather than being forwarded to another Messenger.

@param messenger The Messenger to which Writer Messages will be sent. @throws IllegalArgumentException If the messenger is null. */ public Messenger_Styled_Writer ( Messenger messenger ) {this (messenger, null);} private Messenger_Styled_Writer () {} /*============================================================================== Accessors */ /** Get the Messenger used to send what is written.

@return A Messenger. */ public Messenger Writer_Messenger () {return The_Messenger;} /** Get the message route address list.

@return A Value that contains a copy of the message route address list. */ public Value Route () { try {return new Value (Route);} catch (PVL_Exception exception) {/* Shouldn't happen */} return null; } /** Set the message route address list.

@param route An Array Value containing the route-to address list to be used when sending Messages. If null no message routing will be used. The Value is copied. @return This Messenger_Styled_Writer. @throws PVL_Exception If the route is not an Array of STRING type Values. */ public Messenger_Styled_Writer Route ( Value route ) throws PVL_Exception { if (route == null) Route.Remove_All (); else Route = new Value (Message.Valid_Route (route)); return this; } /** Test if output has been suspended.

@return true if output has been suspended; false otherwise. */ public boolean Suspended () {return Suspended;} /** Turn output suspension on or off.

@param suspend If true subsequent output will be suspended; if false output will occur. @return This Messenger_Styled_Writer. */ public Messenger_Styled_Writer Suspend ( boolean suspend ) {Suspended = suspend; return this;} /** Test if this Messenger_Styled_Writer is capable of sending write messages.

@return true if the {@link #Writer_Messenger() Messenger} bound to this Messenger_Styled_Writer is no longer {@link Messenger#Is_Connected() connected} to it communication channel; false if this Messenger_Styled_Writer is currently able to write to its Messenger. */ public boolean Closed () {return ! The_Messenger.Is_Connected ();} /*============================================================================== Writer */ /** Write a portion of an array of characters.

If the characters array is null or zero length, or the amount to write is zero, nothing is done.

@param characters The char array containing the characters to be written. If null or empty nothing is done. @param offset Array offset from which to start writing characters. @param amount The number of characters to write. If zero nothing is done. @throws IndexOutOfBoundsException If the offset or amount are negative or the offset plus the amount is greater than the length of the array. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public void write ( char[] characters, int offset, int amount ) throws IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Messenger_Styled_Writer.write: " + offset + '/' + amount + " -" + NL + characters); if (characters == null || characters.length == 0 || amount == 0) { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Nothing to write." + NL +"<<< Messenger_Styled_Writer.write"); return; } if (offset < 0 || amount < 0 || (offset + amount) > characters.length) throw new IndexOutOfBoundsException (ID + NL + "Invalid values for " + characters.length + " character array content -" + NL + " Array offset " + offset + " and amount " + amount + '.'); Send (new String (characters, offset, amount)); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Messenger_Styled_Writer.write"); } /** Write an array of characters.

If the characters array is null or zero length nothing is done.

@param characters The char array containing the characters to be written. If null or empty nothing is done. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public void write ( char[] characters ) throws IOException { if (characters == null) return; Send (new String (characters)); } /** Write a character.

N.B.: Characters are not buffered so writing characters one at a time, rather than {@link #write(char[]) as an array} or {@link #write(String) string}, is very inefficient.

@param character The character to be written in the 16 low-order bits. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public void write ( int character ) throws IOException { char[] array = new char[1]; array[0] = (char)character; Send (new String (array)); } /** Write a portion of a String.

If the string is null or zero length, or the amount to write is zero, nothing is done.

@param string The String containing the characters to be written. If null or empty nothing is done. @param offset String offset from which to start writing characters. @param amount The number of characters to write. If zero nothing is done. @throws IndexOutOfBoundsException If the offset or amount are negative or the offset plus the amount is greater than the length of the string. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public void write ( String string, int offset, int amount ) throws IOException { if (string == null || amount == 0) return; if (offset < 0 || amount < 0 || (offset + amount) > string.length ()) throw new IndexOutOfBoundsException (ID + NL + "Invalid values for " + string.length () + " character String content -" + NL + " String offset " + offset + " and amount " + amount + '.'); Send (string.substring (offset, offset + amount)); } /** Write a String.

@param string The String containing the characters to be written. If null or empty nothing is done. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public void write ( String string ) throws IOException { if (string == null) return; Send (string); } /** Flush the writers.

Because no buffering is done flush has no effect. */ public void flush () {} /** Close the writer.

This method has no effect. Because writing is done by sending Messages via the {@link #Writer_Messenger() Messenger} bound to this Messenger_Styled_Writer, and the Messenger may be in use by other objects, its communication channel should not be disconnected by the Messenger_Styled_Writer. To disconnect the Messenger tell it that it is {@link Messenger#Done(String) done}.

@see #Closed() */ public void close () {} /*============================================================================== Styled_Writer */ /** Write a styled String.

The AttributeSet used to specify the display style may be generated using the StyleConstants functions. For example:


SimpleAttributeSet style;
StyleConstants.setBold (style, true);
StyleConstants.setForeground (style, Color.RED);

The following styles, indicated by their StyleConstants functions, are recognized by a Messenger_Styled_Writer:

  • setAlignment
  • setBackground
  • setBidiLevel
  • setBold
  • setFirstLineIndent
  • setFontFamily
  • setFontSize
  • setForeground
  • setItalic
  • setLeftIndent
  • setLineSpacing
  • setRightIndent
  • setSpaceAbove
  • setSpaceBelow
  • setStrikeThrough
  • setSubscript
  • setSuperscript
  • setTabSet
  • setUnderline

@param string The String containing the characters to be written. If null or empty nothing is done. @param style An AttributeSet describing the style to be used when displaying the text. If null, no style will be applied. @throws IOException If the Write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public Messenger_Styled_Writer Write ( String string, AttributeSet style ) throws IOException { if (string == null) return this; Send (string, style); return this; } /** Write a plain String.

Using this method is the same a using {@link #Write(String, AttributeSet)} with a null style, or using (@link #write(String)}.

@param string The String containing the characters to be written. If null or empty nothing is done. @throws IOException If the write message could not be sent. This will be an EOFException if this Messenger_Styled_Writer is {@link #Closed() closed}. */ public Messenger_Styled_Writer Write ( String string ) throws IOException { if (string == null) return this; Send (string); return this; } /*============================================================================== Messenger */ private void Send ( String string ) throws IOException {Send (string, null);} private void Send ( String string, AttributeSet style ) throws IOException { if (Suspended || string.length () == 0) return; if ((DEBUG & (DEBUG_SEND | DEBUG_WRITE)) != 0) System.out.println (">>> Messenger_Styled_Writer.Send: " + NL + '"' + string + '"'); if (Closed ()) throw new EOFException (ID + NL + "The Messenger communication channel connection is closed."); Message message = Message.Action (WRITE_ACTION) .Set (WRITTEN_PARAMETER_NAME, new String_Buffer (string).special_to_escape ().toString ()) .Add (Parameter_Style (style)); try { message.Route_To (Route); if ((DEBUG & (DEBUG_SEND | DEBUG_WRITE)) != 0) System.out.println (" Sending to " + Route.Description () + NL + message); The_Messenger.Send (message); } catch (PVL_Exception exception) { throw new IOException (ID + NL + "Unable to generate the PVL Message content for" + NL + '"' + string + '"' + NL + exception.getMessage ()); } if ((DEBUG & (DEBUG_SEND | DEBUG_WRITE)) != 0) System.out.println ("<<< Messenger_Styled_Writer.Send"); } /*------------------------------------------------------------------------------ Parameter to/from AttributeSet Conversion */ /** Generate a Parameter that specifies a set of text styles.

The set of style attributes is represented by a {@link #STYLE_PARAMETER_GROUP} Parameter Aggregate in which each style attribute is reprented by Pararmeter tht the name of the attribute and its Assignment Value has the value of the attribute.

@param style An AttributeSet containing a set of text style attributes. If null, null is returned. @return A Parameter Aggregate specifying each style attribute. @see #Style_Parameter(Parameter) */ public static Parameter Parameter_Style ( AttributeSet style ) { if (style == null) return null; Parameter parameter, group = new Parameter (STYLE_PARAMETER_GROUP) .Classification (Parameter.GROUP); String name; Enumeration names = style.getAttributeNames (); while (names.hasMoreElements ()) { Object object = names.nextElement (); parameter = new Parameter (object.toString ()); object = style.getAttribute (object); try { if (object instanceof Color) { parameter.Value (new Value ((long)((Color)object).getRGB () & 0xFFFFFFFFL).Base (16)); } else if (object instanceof Boolean) parameter.Value (new Value (object.toString ()).Type (Value.IDENTIFIER)); else if (object instanceof TabSet) { Value value = new Value ().Type (Value.SEQUENCE); TabSet tab_set = (TabSet)object; TabStop tab_stop; for (int index = 0, total = tab_set.getTabCount (); index < total; index++) { tab_stop = tab_set.getTab (index); value .Add (new Value ().Type (Value.SEQUENCE) .Add (new Value (tab_stop.getPosition ())) .Add (new Value (tab_stop.getAlignment ())) .Add (new Value (tab_stop.getLeader ()))); } parameter.Value (value); } else // String, Integer or Float. parameter.Value (new Value (object)); group.Add (parameter); } catch (PVL_Exception exception) {/* Ignore problem cases */} } return group; } private static final int ALIGNMENT_CODE = 0, BACKGROUND_CODE = 1, BIDI_LEVEL_CODE = 2, BOLD_CODE = 3, FIRST_LINE_INDENT_CODE = 4, FONT_FAMILY_CODE = 5, FONT_SIZE_CODE = 6, FOREGROUND_CODE = 7, ITALIC_CODE = 8, LEFT_INDENT_CODE = 9, LINE_SPACING_CODE = 10, RIGHT_INDENT_CODE = 11, SPACE_ABOVE_CODE = 12, SPACE_BELOW_CODE = 13, STRIKETHROUGH_CODE = 14, SUBSCRIPT_CODE = 15, SUPERSCRIPT_CODE = 16, TAB_SET_CODE = 17, UNDERLINE_CODE = 18; /** Recognized {@link #STYLE_PARAMETER_GROUP} parameter names. */ public static final String ALIGNMENT_NAME = StyleConstants.Alignment.toString (), BACKGROUND_NAME = StyleConstants.Background.toString (), BIDI_LEVEL_NAME = StyleConstants.BidiLevel.toString (), BOLD_NAME = StyleConstants.Bold.toString (), FIRST_LINE_INDENT_NAME = StyleConstants.FirstLineIndent.toString (), FONT_FAMILY_NAME = StyleConstants.FontFamily.toString (), FONT_SIZE_NAME = StyleConstants.FontSize.toString (), FOREGROUND_NAME = StyleConstants.Foreground.toString (), ITALIC_NAME = StyleConstants.Italic.toString (), LEFT_INDENT_NAME = StyleConstants.LeftIndent.toString (), LINE_SPACING_NAME = StyleConstants.LineSpacing.toString (), RIGHT_INDENT_NAME = StyleConstants.RightIndent.toString (), SPACE_ABOVE_NAME = StyleConstants.SpaceAbove.toString (), SPACE_BELOW_NAME = StyleConstants.SpaceBelow.toString (), STRIKETHROUGH_NAME = StyleConstants.StrikeThrough.toString (), SUBSCRIPT_NAME = StyleConstants.Subscript.toString (), SUPERSCRIPT_NAME = StyleConstants.Superscript.toString (), TAB_SET_NAME = StyleConstants.TabSet.toString (), UNDERLINE_NAME = StyleConstants.Underline.toString (); private static Hashtable Style_Name_Codes = new Hashtable (); static { Style_Name_Codes.put (ALIGNMENT_NAME, new Integer (ALIGNMENT_CODE)); Style_Name_Codes.put (BACKGROUND_NAME, new Integer (BACKGROUND_CODE)); Style_Name_Codes.put (BIDI_LEVEL_NAME, new Integer (BIDI_LEVEL_CODE)); Style_Name_Codes.put (BOLD_NAME, new Integer (BOLD_CODE)); Style_Name_Codes.put (FIRST_LINE_INDENT_NAME, new Integer (FIRST_LINE_INDENT_CODE)); Style_Name_Codes.put (FONT_FAMILY_NAME, new Integer (FONT_FAMILY_CODE)); Style_Name_Codes.put (FONT_SIZE_NAME, new Integer (FONT_SIZE_CODE)); Style_Name_Codes.put (FOREGROUND_NAME, new Integer (FOREGROUND_CODE)); Style_Name_Codes.put (ITALIC_NAME, new Integer (ITALIC_CODE)); Style_Name_Codes.put (LEFT_INDENT_NAME, new Integer (LEFT_INDENT_CODE)); Style_Name_Codes.put (LINE_SPACING_NAME, new Integer (LINE_SPACING_CODE)); Style_Name_Codes.put (RIGHT_INDENT_NAME, new Integer (RIGHT_INDENT_CODE)); Style_Name_Codes.put (SPACE_ABOVE_NAME, new Integer (SPACE_ABOVE_CODE)); Style_Name_Codes.put (SPACE_BELOW_NAME, new Integer (SPACE_BELOW_CODE)); Style_Name_Codes.put (STRIKETHROUGH_NAME, new Integer (STRIKETHROUGH_CODE)); Style_Name_Codes.put (SUBSCRIPT_NAME, new Integer (SUBSCRIPT_CODE)); Style_Name_Codes.put (SUPERSCRIPT_NAME, new Integer (SUPERSCRIPT_CODE)); Style_Name_Codes.put (TAB_SET_NAME, new Integer (TAB_SET_CODE)); Style_Name_Codes.put (UNDERLINE_NAME, new Integer (UNDERLINE_CODE)); } private static int Style_Name_Code ( String style_name ) { Integer code = Style_Name_Codes.get (style_name); if (code == null) return -1; return code.intValue (); } /** Generate a SimpleAttributeSet from a Parameter Aggregate that specifies a set of text style attributes.

The name of each Parameter in the Aggregate is mapped to a StyleConstants function that is used to set an attribute in a SimpleAttributeSet using the Value of the Parameter. The following attribute names are recognized as text style specification Parameter names:

  • {@link #ALIGNMENT_NAME}
  • {@link #BACKGROUND_NAME}
  • {@link #BIDI_LEVEL_NAME}
  • {@link #BOLD_NAME}
  • {@link #FIRST_LINE_INDENT_NAME}
  • {@link #FONT_FAMILY_NAME}
  • {@link #FONT_SIZE_NAME}
  • {@link #FOREGROUND_NAME}
  • {@link #ITALIC_NAME}
  • {@link #LEFT_INDENT_NAME}
  • {@link #LINE_SPACING_NAME}
  • {@link #RIGHT_INDENT_NAME}
  • {@link #SPACE_ABOVE_NAME}
  • {@link #SPACE_BELOW_NAME}
  • {@link #STRIKETHROUGH_NAME}
  • {@link #SUBSCRIPT_NAME}
  • {@link #SUPERSCRIPT_NAME}
  • {@link #TAB_SET_NAME}
  • {@link #UNDERLINE_NAME}

    Parameters that have unrecognized names or that are not an Assignment of the appropriate Value are ignored.

    @param group A Parameter Aggregate containing a list of text style specification Parameters. @return A SimpleAttributeSet contained a set of text styles. @see #Parameter_Style(AttributeSet) */ public static SimpleAttributeSet Style_Parameter ( Parameter group ) { if (group == null || ! group.Is_Aggregate ()) return null; SimpleAttributeSet style = new SimpleAttributeSet (); Parameter parameter; Iterator parameters = group.iterator (); while (parameters.hasNext ()) { parameter = (Parameter)parameters.next (); try { switch (Style_Name_Code (parameter.Name ())) { case ALIGNMENT_CODE: StyleConstants.setAlignment (style, (int)parameter.Value ().long_Data ()); break; case BACKGROUND_CODE: StyleConstants.setBackground (style, new Color ((int)parameter.Value ().long_Data ())); break; case BIDI_LEVEL_CODE: StyleConstants.setBidiLevel (style, (int)parameter.Value ().long_Data ()); break; case BOLD_CODE: StyleConstants.setBold (style, parameter.Value ().String_Data ().equals ("true")); break; case FIRST_LINE_INDENT_CODE: StyleConstants.setFirstLineIndent (style, (float)parameter.Value ().double_Data ()); break; case FONT_FAMILY_CODE: StyleConstants.setFontFamily (style, parameter.Value ().String_Data ()); break; case FONT_SIZE_CODE: StyleConstants.setFontSize (style, (int)parameter.Value ().long_Data ()); break; case FOREGROUND_CODE: StyleConstants.setForeground (style, new Color ((int)parameter.Value ().long_Data ())); break; case ITALIC_CODE: StyleConstants.setItalic (style, parameter.Value ().String_Data ().equals ("true")); break; case LEFT_INDENT_CODE: StyleConstants.setLeftIndent (style, (float)parameter.Value ().double_Data ()); break; case LINE_SPACING_CODE: StyleConstants.setLineSpacing (style, (float)parameter.Value ().double_Data ()); break; case RIGHT_INDENT_CODE: StyleConstants.setRightIndent (style, (float)parameter.Value ().double_Data ()); break; case SPACE_ABOVE_CODE: StyleConstants.setSpaceAbove (style, (float)parameter.Value ().double_Data ()); break; case SPACE_BELOW_CODE: StyleConstants.setSpaceBelow (style, (float)parameter.Value ().double_Data ()); break; case STRIKETHROUGH_CODE: StyleConstants.setStrikeThrough (style, parameter.Value ().String_Data ().equals ("true")); break; case SUBSCRIPT_CODE: StyleConstants.setSubscript (style, parameter.Value ().String_Data ().equals ("true")); break; case SUPERSCRIPT_CODE: StyleConstants.setSuperscript (style, parameter.Value ().String_Data ().equals ("true")); break; case TAB_SET_CODE: Vector tab_stops = new Vector (); Value args, value; Iterator values = parameter.Value ().iterator (); while (values.hasNext ()) { args = (Value)values.next (); if ((value = args.Get (0)) == null) continue; float position = (float)value.double_Data (); if ((value = args.Get (1)) == null) continue; int alignment = (int)value.long_Data (); if ((value = args.Get (2)) == null) continue; int leader = (int)value.long_Data (); tab_stops.add (new TabStop (position, alignment, leader)); } TabStop[] tabs = new TabStop[tab_stops.size ()]; StyleConstants.setTabSet (style, new TabSet ((TabStop[])tab_stops.toArray (tabs))); break; case UNDERLINE_CODE: StyleConstants.setUnderline (style, parameter.Value ().String_Data ().equals ("true")); break; } } catch (PVL_Exception exception) {/* Ignore problem cases */} } return style; } } pirl-2.3.8/PIRL/Messenger/Makefile0000644000175000017500000000075011022437652016506 0ustar mathieumathieu# Makefile for Java Messenger package. # # PIRL CVS ID: Makefile,v 1.4 2008/06/07 07:58:34 castalia Exp # gmake syntax JPATH ?= ../.. JC ?= javac CLASSES := Message.class \ Messenger.class \ Messenger_Styled_Writer.class \ Message_Delivered_Listener.class \ Message_Delivered_Event.class \ Dispatcher.class # Targets: all: classes classes: $(CLASSES) clean: rm -f *.class .SUFFIXES: .java .class .java.class: $(JC) -classpath $(JPATH) $(JFLAGS) $< pirl-2.3.8/PIRL/Messenger/Message_Delivered_Listener.java0000644000175000017500000000424711742734030023131 0ustar mathieumathieu/* Message_Delivered_Listener PIRL CVS ID: Message_Delivered_Listener.java,v 1.7 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import java.util.EventListener; /** A Message_Delivered_Listener defines the interface used to deliver a Message_Delivered_Event.

    @author Bradford Castalia - UA/PIRL @version 1.7 @see Message_Delivered_Event */ public interface Message_Delivered_Listener extends EventListener { /** Deliver a Message_Delivered_Event.

    @param event The Message_Delivered_Event containing the Message to be delivered and a reference to the delivering Messenger. */ public void Message_Delivered ( Message_Delivered_Event event ); /** Get the identity of the listener.

    The Message is expected to contain at least the following parameters:

    {@link Message#ACTION_PARAMETER_NAME}
    The value should be {@link Message#IDENTITY_ACTION}.
    {@link Message#NAME_PARAMETER_NAME}
    The value should be the name by which the object is known.
    Any additional parameters that provide identification information may also be included.

    @return A Message containing parameters describing the identification of the Message_Delivered_Listener object. */ public Message Listener_Identity (); } pirl-2.3.8/PIRL/Messenger/Message.java0000644000175000017500000013726311742734030017306 0ustar mathieumathieu/* Message PIRL CVS ID: Message.java,v 1.50 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.PVL.Parser; import PIRL.PVL.Lister; import PIRL.PVL.Selection; import PIRL.PVL.PVL_Exception; import PIRL.Utilities.ByteBuffer_InputStream; import PIRL.Strings.String_Buffer; import java.nio.ByteBuffer; import java.io.InputStreamReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; /** A Message contains a message and its routing information.

    A Message is a Parameter Aggregate and the message routing information. A Message is typically managed by a Messenger.

    @author Bradford Castalia - UA/PIRL @version 1.50 @see Parameter @see Messenger */ public class Message extends Parameter { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Message (1.50 2012/04/16 06:11:36)"; // Common Message content parameters: /** A Message parameter specifying the action associated with the message. */ public static final String ACTION_PARAMETER_NAME = "Action"; /** Identify request Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String IDENTIFY_ACTION = "Identify"; /** Identity Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String IDENTITY_ACTION = "Identity"; /** Class ID parameter name. */ public static final String CLASS_ID_PARAMETER_NAME = "Class_ID"; /** Sender name in an {@link #IDENTIFY_ACTION} or {@link #IDENTITY_ACTION} action Message. */ public static final String NAME_PARAMETER_NAME = "Name"; /** Acknowledgment Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String ACK_ACTION = "ACK"; /** Negative acknowledgment Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String NACK_ACTION = "NACK"; /** End of messaging - no further messages can be expected - Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String DONE_ACTION = "Done"; // Message label parameters: /** Synchronization sequence at the beginning of a trasmitted message.

    @see #Packet_Label(int) */ public static final String START_MARKER = "_Message_"; /** Separates the label {@link #START_MARKER} from its label size value in a transmitted message.

    @see #Packet_Label(int) */ public static final char START_MARKER_DELIMITER = Parser.PARAMETER_NAME_DELIMITER; /** Marks the end of the message label size value and the required message transmission start sequence.

    @see #Packet_Label(int) */ public static final char START_MARKER_END = Parser.STATEMENT_END_DELIMITER; /** A label parameter of a transmitted message with a value that is the number of message label characters to follow.

    @see #Packet_Label(int) */ public static final String CONTENT_AMOUNT_PARAMETER_NAME = "Size"; /** A label parameter of a transmitted message with a value that is the Message {@link #Route_To() route-to list}.

    @see #Packet_Label(int) */ public static final String ROUTE_TO_PARAMETER_NAME = "Route_To"; /** A label parameter of a transmitted message with a value that is the Message {@link #Route_To() route-from list}.

    @see #Packet_Label(int) */ public static final String ROUTE_FROM_PARAMETER_NAME = "Route_From"; // The Message routing address lists. private Value Route_To = null, Route_From = null; /* >>> WARNING <<< The Parser and Lister objects are not static to prevent collisions across threads in the Messenger. */ // PVL Parser for parsing raw byte content. private final Parser PVL_Parser = PVL_Parser (); // PVL Lister for producing raw byte content and String representations. private final Lister PVL_Lister = PVL_Lister (); /** Marks the first character of a Message {@link #PVL() String representation} when there was a problem rendering the Message content into valid PVL syntax. */ public static final char ERROR_MESSAGE_MARKER = Lister.NEW_LINE.charAt (0); private static String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 1, DEBUG_CONTENT = 1 << 2, DEBUG_ROUTING = 1 << 3, DEBUG_FUNCTIONS = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct an empty Message.

    The Message will be an empty Group with emtpy {@link #Route_To() route-to} and {@link #Route_From() route-from} address lists. The Name of the Message will be the {@link Parser#CONTAINER_NAME}. */ public Message () { super (Parser.CONTAINER_NAME); Classification (Parameter.GROUP); try { Route_To = new Value ().Type (Value.SEQUENCE); Route_From = new Value ().Type (Value.SEQUENCE); } catch (PVL_Exception exception) {/* The new Values can accept the Type. */} } /** Construct a Message from a token name.

    A single Token Parameter with a name the same as the token and no Value is added to the new empty Message.

    @param token A Token Parameter name. @see #Message() */ public Message ( String token ) { this (); Add (new Parameter (token)); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Message token:" + NL + this); } /** Construct a copy of another Message.

    The Message Name, Parameter content and {@link #Route_To() route-to} and {@link #Route_From() route-from} address lists are copied into this Message.

    @param message The Message to be copied. @throws PVL_Exception If the message contains an invalid Parameter. */ public Message ( Message message ) throws PVL_Exception { super (message); Route_To = new Value (message.Route_To); Route_From = new Value (message.Route_From); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Message copy:" + NL + this); } /** Construct a Message as a copy of a Parameter.

    The contents of the parameter are copied into an empty Message. If the copied Parameter is an Assignment it becomes the sole Parameter in this Message. If it is an Aggregate its Parameter list becomes the list of Parameters in this Message and its {@link #Name() name} becomes the name of this Message.

    @param parameter The Parameter to copy into the new Message. @throws PVL_Exception If the Parameter is or contains an invalid Parameter. @see #Message() */ public Message ( Parameter parameter ) throws PVL_Exception { this (); if (parameter != null) { if (parameter.Has_List ()) // Copy in the Aggregate list. Add (new Parameter (parameter).List ()) .Name (parameter.Name ()); else if (parameter.Is_Assignment ()) // Copy in the Assignment. Add (new Parameter (parameter)); } } /** Construct a Message from the contents of a ByteBuffer.

    @param content The ByteBuffer containing the Message content. @see #Content(ByteBuffer) @see #Message() */ public Message ( ByteBuffer content ) throws PVL_Exception { this (); Content (content); } /*============================================================================== Accessors */ /** Set the Message content.

    The current Message content is removed and the new content is added by {@link Parser#Parser(InputStream) parsing} the ByteBuffer data. N.B.: The ByteBuffer must be positioned at the beginning of the content data and its limit must be at or beyond the end of the content data. The position and limit will be unchanged when this method completes, whether normally or by throwing an exception.

    The ByteBuffer content data must be PVL formatted parameters. All the parameters found in the content buffer are added to the Message. The Message itself will retain its name.

    @param content The ByteBuffer containing the Message content. If null the current content of this Message is simply emptied. @return This Message with the new content. @throws PVL_Exception If the content can not be parsed by the {@link #PVL_Parser() Message PVL parser}. */ public Message Content ( ByteBuffer content ) throws PVL_Exception { if ((DEBUG & DEBUG_CONTENT) != 0) System.out.println (">>> Message.Content: " + content); Remove_All (); if (content != null) { int position = content.position (); if ((DEBUG & DEBUG_CONTENT) != 0) { System.out.print (" Content " + content + NL +">"); while (content.hasRemaining ()) System.out.print ((char)content.get ()); System.out.println ("<"); content.position (position); } PVL_Parser.Reset (); try { PVL_Parser.Set_Reader (new InputStreamReader (new ByteBuffer_InputStream (content), Parser.CHARACTER_ENCODING)); Add (PVL_Parser); } catch (PVL_Exception exception) { content.position (position); throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Unable to parse the Message content -" + NL + Content_String (content) + NL + exception.getMessage () ); } catch (UnsupportedEncodingException exception) { throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Unable to parse the Message content with the \"" + Parser.CHARACTER_ENCODING + "\" character encoding." + ((exception.getMessage () == null) ? "" : NL + exception.getMessage ()) ); } finally { content.position (position); } } if ((DEBUG & DEBUG_CONTENT) != 0) System.out.println (this.toString () + NL +"<<< Message.Content"); return this; } /** Get the String representation of Message content bytes.

    If the content buffer has an accessible backing array its contents, from the array offset to the first byte of the buffer plus the buffer position up to the buffer remaining amount, are converted. Otherwise the buffer content, from the buffer position up to the buffer remaining amount, are copied into a temporary array for conversion.

    The byte buffer content array is converted to a new String using the PVL {@link Parser#CHARACTER_ENCODING}.

    @param content A ByteBuffer containing the remaining bytes starting at its current position to be converted to a String. @return A String representation of the buffer content. This will be null if the content is null or the content could not be converted. */ public static String Content_String ( ByteBuffer content ) { String string = null; if (content != null) { byte[] bytes; int offset = 0, amount = content.remaining (); if (content.hasArray ()) { bytes = content.array (); offset = content.arrayOffset () + content.position (); } else { content = content.slice (); bytes = new byte[amount]; while (content.hasRemaining ()) bytes[offset++] = content.get (); offset = 0; } try {string = new String (bytes, offset, amount, Parser.CHARACTER_ENCODING);} catch (UnsupportedEncodingException exception) {/* If the Parser encoding is not supported we're SOL */} } return string; } /** Get the Message content as a transmittable packet.

    A Message packet is a ByteBuffer that contains Message routing information and Message content in a form that can be transmitted over a stream.

    The packet begins with a set of {@link #Packet_Label(int) label} parameters. The packet label is followed by the Message content as {@link #PVL() PVL formatted text}.

    All content is US_ASCII encoded that ensurces that 7-bit characters, one character per byte, will be used for the PVL text.

    @return A ByteBuffer containing the Message label and content data. @throws PVL_Exception If a valid PVL representation can not be produced for the Message content. @see #Packet_Label(int) */ public ByteBuffer Packet () throws PVL_Exception { // Content description. String content = PVL (); if (content.length () > 0 && content.charAt (0) == ERROR_MESSAGE_MARKER) { throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Unable to generate the Message packet PVL." + NL + content); } // Combine the Message label and content into a byte array. byte[] bytes; try {bytes = (Packet_Label (content.length ()) + content) .getBytes ("US-ASCII");} catch (java.io.UnsupportedEncodingException exception) { throw new PVL_Exception (ID, PVL_Exception.ILLEGAL_SYNTAX, "Unable to encode the Message packet PVL as US-ASCII bytes." + NL + ((exception.getMessage () == null) ? "" : (exception.getMessage () + NL)) + content); } return ByteBuffer.wrap (bytes); } /** Get the message transmission packet label for a Message

    A Message label section contains PVL with the following parts:

    {@link #START_MARKER}
    Used as a message synchronization marker to locate the beginning of a message in a byte stream.
    {@link #START_MARKER_DELIMITER}
    Separates the {@link #START_MARKER} from the label size value that immediately follows. There are no spaces around the {@link #START_MARKER_DELIMITER} character ('=') that separates the parameter name from its value.
    Label size
    The number of Message label characters that follow. N.B. the label size does not include the {@link #START_MARKER}, {@link #START_MARKER_DELIMITER}, the label size representation characters nor the {@link #START_MARKER_END} that immediately follows.
    {@link #START_MARKER_END}
    Marks the end of the label size value representation characters. There is no end-of-line sequence following this marker.
    {@link #CONTENT_AMOUNT_PARAMETER_NAME}
    A parameter with the {@link #CONTENT_AMOUNT_PARAMETER_NAME} name and the content amount as its assigned value. An {@link Lister#NEW_LINE end-of-line sequence} ends the parameter.
    {@link #ROUTE_TO_PARAMETER_NAME}
    A parameter with the {@link #ROUTE_TO_PARAMETER_NAME} name and the {@link #Route_To() route-to list} of addresses as its assigned value array. An {@link Lister#NEW_LINE end-of-line sequence} ends the parameter. If this Message has an empty route-to list this parameter is not included in the label.
    {@link #ROUTE_FROM_PARAMETER_NAME}
    A parameter with the {@link #ROUTE_FROM_PARAMETER_NAME} name and the {@link #Route_From() route-from list} of addresses as its assigned value array. An {@link Lister#NEW_LINE end-of-line sequence} ends the parameter. If this Message has an empty route-from list this parameter is not included in the label.
    N.B.: The message start synchronization sequence is composed of the START_MARKER through START_MARKER_END characters. This sequence is critical for identifying the start of a message in an unreliable transmitted byte stream.

    @param content_amount The value to be assigned to the {@link #CONTENT_AMOUNT_PARAMETER_NAME}. @return A String containing the Message label section of a message transmission packet. @see #Packet() */ protected String Packet_Label ( int content_amount ) { Message label = new Message () .Set (CONTENT_AMOUNT_PARAMETER_NAME, content_amount); try { if (Route_To.getChildCount () > 0) label.Set (ROUTE_TO_PARAMETER_NAME, Route_To); if (Route_From.getChildCount () > 0) label.Set (ROUTE_FROM_PARAMETER_NAME, Route_From); } catch (PVL_Exception exception) {/* Route_To and Route_From are valid Values */} String label_string = ""; Iterator parameters = label.iterator (); while (parameters.hasNext ()) label_string += ((Parameter)parameters.next ()).Description (PVL_Lister) + Lister.NEW_LINE; return START_MARKER + START_MARKER_DELIMITER + String.valueOf (label_string.length ()) + START_MARKER_END + label_string; } /** Get the value of the {@link #CONTENT_AMOUNT_PARAMETER_NAME} from the Message.

    This convenience method is used to obtain from a Message label the amount of content data that follows the label section.

    @return The value of the {@link #CONTENT_AMOUNT_PARAMETER_NAME}. @throws PVL_Exception If the content amount parameter was not found or its Value could not produce an integer. @see #Packet_Label(int) */ protected int Content_Amount () throws PVL_Exception { Parameter parameter = Find (CONTENT_AMOUNT_PARAMETER_NAME, Parameter.ASSIGNMENT); if (parameter == null) throw new PVL_Exception (ID, PVL_Exception.EMPTY_STATEMENT, "No content amount " + CONTENT_AMOUNT_PARAMETER_NAME + " parameter -" + NL + toString ()); return (int)(parameter.Value ().long_Data ()); } /** Get the Message content as a PVL String representation.

    The Message's PVL {@link Parameter#Description(Lister) description} String is generated using a suitably configured {@link #PVL_Lister() PVL Lister}. If the Name of the Message is not the {@link Parser#CONTAINER_NAME} it is temporarily changed to this special name so a PVL "END" statement will always be provided.

    N.B.: If the first character of the resultant String is the {@link #ERROR_MESSAGE_MARKER} character then a problem was encountered while attempting to generate the PVL syntax from the Message content. In this case the returned String is an error report.

    @return A PVL String representation of the Message content. @see #toString() */ public String PVL () { String name = null; if (! Parser.CONTAINER_NAME.equals (Name ())) { name = Name (); Name (Parser.CONTAINER_NAME); } String string = Description (PVL_Lister); if (name != null) Name (name); return string; } /** Get the Message content as a printable PVL String representation.

    The Message's PVL {@link Parameter#Description() description} String is generated in standard format. Any escape sequences are {@link #Unescape(String) converted} to their original character values. As a result, parameter string values containing new-line sequences, for example, will be printed as multiple lines. If the Message {@link #Name() name} is not the {@link Parser#CONTAINER_NAME} a top-level group with this name will be included in the PVL represenation and no PVL "END" statement will be provided; otherwise the top-level container group will not be included but the "END" statement will be.

    N.B.: If the first character of the resultant String is the {@link #ERROR_MESSAGE_MARKER} character then a problem was encountered while attempting to generate the PVL syntax from the Message content. In this case the returned String is an error report.

    @return A String representation of the Message content. @see #PVL() */ public String toString () {return Unescape (Description ());} /*------------------------------------------------------------------------------ Parameter Values */ /** Get a named parameter value as a String.

    The named Parameter must be an Assignment of a single non-Array Value.

    @param name The String naming the parameter to be found. If null, null will be returned. @return A String representing the value of the parameter. This will be null if the named parameter could not be found, it is not an Assignment or its Value is an Array. N.B. The actual Parameter Value may not be a String in which case its String representation is returned. @see #Value_of(String) */ public String Get ( String name ) { Value value = Value_of (name); if (value != null) { try {return value.String_Data ();} catch (PVL_Exception exception) {/* If the Value is an Array */} } return null; } /** Get the String value of a parameter with a name matching a pattern.

    The matching Parameter must be an Assignment of a single non-Array Value.

    @param pattern A regular expression pattern String. If null, null is returned. @return A String representing the value of the parameter with the name that matches the pattern. This will be null if no matching parameter could be found, it is not an Assignment or its Value is an Array. N.B. The actual Parameter Value may not be a String in which case its String representation is returned. @see #Value_of_Matching(String) */ public String Matching ( String pattern ) { Value value = Value_of_Matching (pattern); if (value != null) { try {return value.String_Data ();} catch (PVL_Exception exception) {/* If the Value is an Array */} } return null; } /** Get a named parameter value as a Value.

    Only an Assignment parameter will be found. The first Assignment parameter with the matching name is returned.

    @param name The String naming the parameter to be found. If null, null is returned. @return The Value of the Parameter. This will be null if the parameter could not be found. @see #Get(String) */ public Value Value_of ( String name ) { if (name != null) { Parameter parameter = Find (name, Parameter.ASSIGNMENT); if (parameter != null) { try {return parameter.Value ();} catch (PVL_Exception exception) {/* Shouldn't happen */} } } return null; } /** Get the Value of a parameter with a name matching a pattern.

    Only an Assignment parameter will be found.

    @param pattern A regular expression pattern String. If null, null is returned. @return The Value of the Parameter with the name that matches the pattern. This will be null if a matching parameter could not be found. */ public Value Value_of_Matching ( String pattern ) { if (pattern != null) { Parameter parameter = Find ( new Parameter (pattern) .Classification (Parameter.ASSIGNMENT), new Selection () .Name (true) .Pattern_Match (true) .And (true) .Classification (true) ); if (parameter != null) { try {return parameter.Value ();} catch (PVL_Exception exception) {/* Shouldn't happen */} } } return null; } /** Set a named parameter value at a list index.

    If a Parameter with the specified name is found its Value is set to the specified value; any existing Value is replaced. If no Parameter is found a new one with the specified Value is added to the parameter list at the specified index.

    The specified value may be any object suitable for providing the {@link Value#Data(Object) data of a Value}. If the specified value is null and an existing Parameter is found, the Parameter is removed. If, however, no Parameter is found a null value does nothing.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A Object that contains the value to be applied, or null if an existing parameter is to be removed. @param index The index in the parameter list at which to insert a new Parameter. If less than zero a new parameter will be added to the beginning of the list (index zero); if greater than or equal to the size of the parameter list a new parameter will be added to the end of the list (index equal to the list size). @return This Message. @throws PVL_Exception If the value is not a suitable Object for a Value. */ public Message Set ( String name, Object value, int index ) throws PVL_Exception { if (name != null) { Parameter parameter = Find (name); if (parameter == null) { if (value != null) { // New parameter. if (index < 0) index = 0; else if (index > List_Size ()) index = List_Size (); Insert (new Parameter (name).Value (value), index); } } else if (! parameter.Is_Aggregate ()) { // Existing Assignment parameter. if (value == null) Remove (parameter); else parameter.Value (value); } } return this; } /** Set a named parameter value.

    If a Parameter with the specified name is found its Value is set to the specified value; any existing Value is replaced. If no Parameter with the specified name is found a new one with the specified value is added to the end of the parameter list.

    The specified value may be any object suitable for providing the {@link Value#Data(Object) data of a Value}. If the specified value is null and an existing Parameter is found, the Parameter is removed. If, however, no Parameter is found a null value does nothing.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A Object that contains the value to be assigned, or null if an existing parameter is to be removed. @return This Message. @throws PVL_Exception If the value is not a suitable Object for a Value. @see #Set(String, Object, int) */ public Message Set ( String name, Object value ) throws PVL_Exception {return Set (name, value, List_Size ());} /** Set a named parameter value.

    This is a convenience method to set a String value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A String value to be assigned, or null if an existing parameter is to be removed. @return This Message. @see #Set(String, Object) */ public Message Set ( String name, String value ) { try {return Set (name, (Object)value);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter value.

    This is a convenience method to set a String value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A String value to be assigned, or null if an existing parameter is to be removed. @param index The index in the parameter list at which to insert a new Parameter. If less than zero a new parameter will be added to the beginning of the list (index zero); if greater than or equal to the size of the parameter list a new parameter will be added to the end of the list (index equal to the list size). @return This Message. @see #Set(String, Object, int) */ public Message Set ( String name, String value, int index ) { try {return Set (name, (Object)value, index);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter value.

    This is a convenience method to set an integer value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A long integer value to be assigned. @return This Message. @see #Set(String, Object) */ public Message Set ( String name, long value ) { try {return Set (name, new Long (value));} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter value.

    This is a convenience method to set an integer value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A long integer value to be assigned. @param index The index in the parameter list at which to insert a new Parameter. If less than zero a new parameter will be added to the beginning of the list (index zero); if greater than or equal to the size of the parameter list a new parameter will be added to the end of the list (index equal to the list size). @return This Message. @see #Set(String, Object, int) */ public Message Set ( String name, long value, int index ) { try {return Set (name, new Long (value), index);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter value.

    This is a convenience method to set a real number value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A double value to be assigned. @return This Message. @see #Set(String, Object) */ public Message Set ( String name, double value ) { try {return Set (name, new Double (value));} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter value.

    This is a convenience method to set a real number value with no possibility of an exception being thrown.

    @param name The name of the Parameter to be assigned the value. If null nothing is done. @param value A double value to be assigned. @param index The index in the parameter list at which to insert a new Parameter. If less than zero a new parameter will be added to the beginning of the list (index zero); if greater than or equal to the size of the parameter list a new parameter will be added to the end of the list (index equal to the list size). @return This Message. @see #Set(String, Object, int) */ public Message Set ( String name, double value, int index ) { try {return Set (name, new Double (value), index);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Set a named parameter token.

    A parameter token has no value. N.B.: If an existing Parameter with the specified name is found it's Value is dropped. If no Parameter with the specified name is found a new one is added to the end of the parameter list.

    @param name The name of the token Parameter. If null nothing is done. @param index The index in the parameter list at which to insert a new Parameter. If less than zero a new parameter will be added to the beginning of the list (index zero); if greater than or equal to the size of the parameter list a new parameter will be added to the end of the list (index equal to the list size). @return This Message. */ public Message Set_Token ( String name, int index ) { if (name != null) { Parameter parameter = Find (name); if (parameter == null) { // New parameter. if (index < 0) index = 0; else if (index > List_Size ()) index = List_Size (); try {Insert (new Parameter (name), index);} catch (PVL_Exception exception) {/* Message is an aggregate */} } else if (! parameter.Is_Aggregate ()) { // Existing Assignment parameter. parameter.Classification (Parameter.TOKEN); } } return this; } /** Set a named parameter token.

    The token will be added to the end of the parameter list.

    @param name The name of the token Parameter. If null nothing is done. @return This Message. @see #Set_Token(String, int) */ public Message Set_Token ( String name ) {return Set_Token (name, List_Size ());} /** Add a parameter to the Message.

    The parameter is copied. The name of the copy is set to the specified name if the specified name is non-null. Then the copy is added to the end of the Message parameter list.

    @param name A String that will be the name of the new parameter. If null, the existing name will be unchanged. @param parameter A Parameter a copy of which will be added to the end of this Message list. If null, nothing is done. @return This Message. */ public Message Add ( String name, Parameter parameter ) { if (parameter != null) { try { // Copy the Parameter. parameter = new Parameter (parameter); if (name != null) // Set the name of the copied parameter. parameter.Name (name); super.Add (parameter); } catch (PVL_Exception exception) {/* Shouldn't happen */} } return this; } /** Add a Parameter to the Message.

    The Parameter is added to the end of the Message Parameter list. N.B.: The Parameter is not copied.

    This is a convenience method to add a Parameter with no possibility of an exception being thrown.

    @param parameter The Parameter to be added to the end of this Message list. If null, nothing is done. @return This Message. @see #Add(String, Parameter) */ public Message Add ( Parameter parameter ) { if (parameter != null) { try {super.Add (parameter);} catch (PVL_Exception exception) {/* Shouldn't happen */} } return this; } /** Remove a named parameter from the Message.

    Any parameter at the specified pathname - relative or absolute - is removed from the Message regardless of its Classification (Token, Assignment or Aggregate). Only the first parameter found at the pathname is removed.

    @param pathname The pathnname String for the parameter to be removed. If null nothing is done. @return The removed Parameter, or null if nothing was removed. @see Parameter#Remove(Parameter) */ public Parameter Remove ( String pathname ) { Parameter parameter = null; if (pathname != null) { parameter = Find (pathname); if (parameter != null) parameter.Parent ().Remove (parameter); } return parameter; } /** Remove all occurances of a named parameter from the Message.

    All parameters at the specified pathname - relative or absolute - are removed from the Message regardless of their Classification (Token, Assignment or Aggregate).

    @param pathname The pathnname String for the parameters to be removed. If null nothing is done. @return true if at least one parameter was removed; false otherwise. @see Parameter#Remove(Parameter) */ public boolean Remove_All ( String pathname ) { boolean removed = false; if (pathname != null) while (Remove (pathname) != null) removed = true; return removed; } /** Get the action associated with this Message.

    If an {@link #ACTION_PARAMETER_NAME} is found its value is returned. Otherwise, if the Message contains at least one parameter and the first parameter is a token (a parameter without a value), then the token name is returned.

    @return A String naming the action associated with this Message. This will be null if no action could be identified. */ public String Action () { String action = Get (ACTION_PARAMETER_NAME); if (action == null && List_Size () >= 1) { Parameter parameter = null; try {parameter = (Parameter)List ().get (0);} catch (PVL_Exception exception) {/* Pre-checked */} if (parameter.Is_Token ()) action = parameter.Name (); } return action; } /*------------------------------------------------------------------------------ Routing */ /** Add an address to the {@link #Route_To() route-to} list.

    @param to A {@link Messenger#Address() Messenger address}. @return This Message. */ public Message To ( String to ) {Put (to, Route_To); return this;} /** Get the next address from the end of the {@link #Route_To() route-to} list.

    The address is not removed from the list.

    @return The address on the end of the route-to list, or null if the list is empty. */ public String To () {return Peek (Route_To);} /** Get the address from the front of the {@link #Route_To() route-to} list.

    The address is not removed from the list.

    @return The address at the front of the route-to list, or null if the list is empty. This should be the address of the destination Messenger where the Message is to be delivered. */ public String Destination_Address () {return First (Route_To);} /** Get the next address off of the end of the {@link #Route_To() route-to} list.

    The address is removed from the list.

    @return The address on the end of the route-to list, or null if the list is empty. */ public String Pop_To () {return Pop (Route_To);} /** Get the Message route-to list.

    The addresses in the list are LIFO ordered: The last entry is the address of the first Messenger to which the Message should be sent (not the Messenger that sends the Message); the first entry is the intended recipient Messenger where the Message is to be delivered. A Message with an empty route-to list will be delivered by the Messenger that receives it.

    @return An Array Value containing zero or more {@link Messenger#Address() Messenger addresses}. This is a copy of the list owned by the Message. @see #Route_To(Value) */ public Value Route_To () { try {return new Value (Route_To);} catch (PVL_Exception exception) {/* Shouldn't happen */} return null; } /** Set the Message route-to list.

    @param to An Array Value containing zero or more {@link Messenger#Address() Messenger addresses}. The Value will be copied into this Message replacing the previous route-to list. If the Value is null the current list is emptied. @return This Message. @throws PVL_Exception If the Value is not an Array of STRING type Values. */ public Message Route_To ( Value to ) throws PVL_Exception { if (to == null) Route_To.Remove_All (); else Route_To = new Value (Valid_Route (to)); return this; } /** Clear the Message route-to list.

    @return This Message. */ public Message Clear_Route_To () { Route_To.Remove_All (); return this; } /** Apply the {@link #Route_To() route-to list} from another Message to this Message.

    @param message The Message from which to obtain the route-to list. If the message is the same as this Message, nothing is done. @return This Message. */ public Message Route_To ( Message message ) { if (message != null && message != this) // Copy the other Route_To to this Route_To. Route_To = message.Route_To (); return this; } /** Get the count of {@link #Route_To() route-to} adresses.

    @return The number of addresses on the route-to list. */ public int Route_To_Count () {return Route_To.Array_Size ();} /** Add an address to the {@link #Route_From() route-from} list.

    @param from A {@link Messenger#Address() Messenger address}. @return This Message. */ public Message From ( String from ) {Put (from, Route_From); return this;} /** Get the next address from the end of the {@link #Route_From() route-from} list.

    The address is not removed from the list.

    @return The address on the end of the route-from list, or null if the list is empty. This will be the address of the last Messenger to have sent the Message. */ public String From () {return Peek (Route_From);} /** Get the address from the front of the {@link #Route_From() route-from} list.

    The address is not removed from the list.

    @return The address at the front of the route-from list, or null if the list is empty. This should be the address of the source Messenger where the Message originated. */ public String Source_Address () {return First (Route_From);} /** Get the next address off of the end of the {@link #Route_From() route-from} list.

    The address is removed from the list.

    @return The address on the end of the route-from list, or null if the list is empty. */ public String Pop_From () {return Pop (Route_From);} /** Get the Message route-from list.

    Each Messenger that sends a Message adds its adress to the end of the list.

    @return An Array Value containing zero or more {@link Messenger#Address() Messenger addresses}. This is a copy of the list owned by the Message. @see #Route_From(Value) */ public Value Route_From () { try {return new Value (Route_From);} catch (PVL_Exception exception) {/* Shouldn't happen */} return null; } /** Set the Message route-from list.

    @param from An Array Value containing zero or more {@link Messenger#Address() Messenger addresses}. The Value will be copied into this Message replacing the previous route-to list. If the Value is null the current list is emptied. @return This Message. @throws PVL_Exception If the Value is not an Array of STRING type Values. */ public Message Route_From ( Value from ) throws PVL_Exception { if (from == null) Route_From.Remove_All (); else Route_From = new Value (Valid_Route (from)); return this; } /** Clear the Message route-from list.

    @return This Message. */ public Message Clear_Route_From () { Route_From.Remove_All (); return this; } /** Get the count of {@link #Route_From() route-from} adresses.

    @return The number of addresses on the route-from list. */ public int Route_From_Count () {return Route_From.Array_Size ();} /** Get the routing description.

    @return A String containing a Route To: address list followed by a Route From: address list. */ public String Routing () { String_Buffer string = new String_Buffer (); return " Route To: " + new String_Buffer (Route_To.Description ()) .replace_span (0, "()", "").toString () + NL + "Route From: " + new String_Buffer (Route_From.Description ()) .replace_span (0, "()", "").toString (); } /** Set a Message to be routed back to the sender of another Message.

    The {@link #Route_To(Value) route-to} list is of this Message is set to the {@link #Route_From() route-from} list of the other Message. The route-from list of this Message is emptied.

    @param message The Message to receive a reply. @return This Message. */ public Message Reply_To ( Message message ) { if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (">>> Message.Reply_To:" + NL +" Route_From " + message.Route_From.Description ()); if (this == message) // Move Route_From to Route_To. Route_To = Route_From; else // Copy the other Route_From to this Route_To. Route_To = message.Route_From (); // Empty Route_From. try {Route_From = new Value ().Type (Value.SEQUENCE);} catch (PVL_Exception exception) {/* Shouldn't happen */} if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (" ->Route_To " + Route_To.Description () + NL +"<<< Message.Reply_To"); return this; } /** Put a String Value in an Array.

    @param string The String to be added as a Value of the array. @param array The Array Value to which to add the string. */ private static void Put ( String string, Value array ) { if (string != null && string.length () != 0) { try {array.Add (string);} catch (PVL_Exception exception) {/* Shouldn't happen */} } } /** Peek at the last Value of an Array.

    The last Value is not removed from the Array.

    @param array The Array Value to examine. @return A String representing the last value of the array. This will be null if the array is empty or the last value can not be represented as a String. */ private static String Peek ( Value array ) { if (array.getChildCount () > 0) { try {return array.Get (array.getChildCount () - 1).String_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } return null; } /** Pop the last Value off of an Array.

    The last Value is removed from the Array.

    @param array The Array Value to examine. @return A String representing the last value of the array. This will be null if the array is empty or the last value can not be represented as a String. */ private static String Pop ( Value array ) { if (array.getChildCount () > 0) { try {return array.Remove (array.getChildCount () - 1).String_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } return null; } /** Get the first Value of an Array.

    The Value is not removed from the Array.

    @param array The Array Value to examine. @return A String representing the first value of the array. This will be null if the array is empty or the first value can not be represented as a String. */ private static String First ( Value array ) { if (array.getChildCount () > 0) { try {return array.Get (0).String_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } return null; } /** Check that a Value is a valid routing address list.

    A valid routing address list is an Array of STRING Type Values.

    @param value The Value to be checked. May not be null, but may be an empty Array. @throws PVL_Exception If the Value is not an Array or one of its Value's is not a STRING Type. */ public static Value Valid_Route ( Value value ) throws PVL_Exception { if (! value.Is_Array ()) throw new PVL_Exception (ID, PVL_Exception.INCOMPATIBLE_TYPES, "Routing Value is not an Array."); Iterator values = value.iterator (); while (values.hasNext ()) if (! ((Value)values.next ()).Is_String ()) throw new PVL_Exception (ID, PVL_Exception.INCOMPATIBLE_TYPES, "Routing Array contains a Value that is not a STRING type."); return value; } /*------------------------------------------------------------------------------ Lister and Parser */ /** Get a PVL Lister configured for Message content representation.

    The Lister is configured with {@link Lister#Verbatim_Strings(boolean) verbatim strings} enabled, {@link Lister#Indent(boolean) indenting} disabled and {@link Lister#Indent_Arrays(boolean) array indenting} disabled. The Lister has no writer bound to it.

    @return A PVL Lister. @see Lister */ public static Lister PVL_Lister () { return new Lister () .Verbatim_Strings (true) .Indent (false) .Indent_Arrays (false); } /** Get a PVL Parser configured for parsing an input stream into Message content.

    The Parser has no source reader bound to it. It has default syntax modes set except: {@link Parser#Verbatim_Strings(boolean) verbatim strings} is enabled and {@link Parser#String_Continuation(boolean) string continuation} and {@link Parser#Filter_Input(boolean) input filtering} are disabled.

    N.B.: The Reader that will be bound to the Parser is excpected to employ "US-ASCII" character encoding. Multi-byte characters may produce unexpected results.

    N.B.: If the Parser is to be reused with more than one parsing sequence - such as with different input readers - it must be {@link Parser#Reset() reset} before each reuse.

    @return A PVL Parser suitable for parsing an input stream into a Message. @see Parser @see Parser#Set_Reader(Reader, long) */ public static Parser PVL_Parser () { Parser parser = new Parser (); parser .Verbatim_Strings (true) .String_Continuation (false) .Filter_Input (false); return parser; } /*============================================================================== Common Messages */ /** Get a Message intitialized with an {@link #IDENTIFY_ACTION} {@link #ACTION_PARAMETER_NAME}.

    @return A Message. */ public static Message Identify () {return Message.Action (IDENTIFY_ACTION);} /** Get a Message intitialized with an {@link #IDENTITY_ACTION} {@link #ACTION_PARAMETER_NAME} and a {@link #NAME_PARAMETER_NAME} with a specified name value.

    @param name The name String to be assigned as the value of the {@link #NAME_PARAMETER_NAME}. @return A Message. */ public static Message Identity ( String name ) {return Message.Action (IDENTITY_ACTION).Set (NAME_PARAMETER_NAME, name);} /** Get a Message intitialized with an {@link #IDENTITY_ACTION} {@link #ACTION_PARAMETER_NAME}.

    @return A Message. */ public static Message Identity () {return Message.Action (IDENTITY_ACTION);} /** Get a Message intitialized with a {@link #NACK_ACTION} {@link #ACTION_PARAMETER_NAME}.

    @return A Message. */ public static Message NACK () {return Message.Action (NACK_ACTION);} /** Get a Message intitialized with a {@link #DONE_ACTION} {@link #ACTION_PARAMETER_NAME}.

    @return A Message. */ public static Message Done () {return Message.Action (DONE_ACTION);} /** Get a Message intitialized with an {@link #ACTION_PARAMETER_NAME}.

    @param action The String specifying the {@link #ACTION_PARAMETER_NAME} value. @return A Message. */ public static Message Action ( String action ) { if (action == null || action.length () == 0) return null; Message message = new Message (); try { message.Add (new Parameter (ACTION_PARAMETER_NAME).Value (action)); if ((DEBUG & DEBUG_FUNCTIONS) != 0) System.out.println (">-< Message.Action: " + action + NL + message); } catch (PVL_Exception exception) {/* Shouldn't happen */} return message; } /*============================================================================== Utilities */ /** Replace escape sequences in a String with their unescaped equivalents.

    When Message content contains parameters with multi-line report text, the end-of-line sequences are converted to escape sequences for transmision. This applies to other special characters such as horizontal tabs. Before printing such report text they should have any escape sequences converted back to their original character values.

    @param string A String that may contain escape sequences. @return A copy of the string argument with all escape sequences converted to their original character values. */ public static String Unescape ( String string ) {return String_Buffer.escape_to_special (string);} } pirl-2.3.8/PIRL/Messenger/Messenger.java0000644000175000017500000025676411742734030017662 0ustar mathieumathieu/* Messenger PIRL CVS ID: Messenger.java,v 1.65 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import PIRL.PVL.PVL_Exception; import PIRL.Utilities.Host; import java.nio.channels.SocketChannel; import java.nio.channels.AsynchronousCloseException; import java.nio.ByteBuffer; import java.io.IOException; import java.io.EOFException; import java.io.InterruptedIOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.ConnectException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Vector; /** A Messenger sends and receives Messages over a communication channel and routes received messages to their destination.

    Client Communication

    A Messenger has a client communication channel to which Messages may be sent and from which they may be received. Messages may be received synchronously or asynchronously. A message that is received synchronously is returned directly to the caller. A timeout for synchronous message receipt may be specified to prevent indefinate blocking if no message arrives. Because only one communication channel is used for receiving messages synchronous receiving of messages can not be done while messages are being received asynchronously. Messages are received asynchronously on a separate message reader thread which waits indefinately until a message arrives and then dispatches the message based on its address routing information.

    Sending and receiving of messages is thread safe. A Message will be completely sent before another will be allowed to be sent. A Message will be completely received and delivered before another will be received. A Message may be sent by one thread while a Message is being received and delivered by another thread. While a Message is being sent or received and delivered another thread trying to do the same operation will be blocked until the current operation completes. Message level synchronization ensures that Messages will not collide and be corrupted during transmission.

    The client communication channel may be closed at any time at either end of the channel. When the channel is closed an explanation may be provided which is then delivered in a Message to the Messenger Employer. This offers the employer an opportunity to take appropriate action. Once the client communication channel is closed in can not be reopened. To reopen communication with the client a new Messenger must be constructed.

    Message Routing

    Messages received asynchronously are examined for routing information. Each Messenger has a unique address which is always added to a Message's route-from list when it is sent. On receipt if the last entry of a Message's route-to list is the address of the receiving Messenger, or the route-to list is empty, the Message is delivered to the Messenger employer. If the address matches that of a forwarding Messenger it is used to send the Message. If the address is unknown the Message is sent back with an appropriate undeliverable notation.

    Message Content

    In addition to routing information contained in the Message label, the Message body may have any content that can be represented using Parameter Value Language (PVL) formatting.

    The input stream that is read is assumed to have one character per byte. This is because a Message is based on PVL formatted syntax using US-ASCII encoded text. The {@link Message#Packet_Label(int) message start synchronization sequence}, which is critical for identifying the location of a message in the communication channel byte stream, must be composed of the required ASCII characters if it is to be recognized (this is done automatically by the Message class). Multi-byte message body characters and binary data may be transmitted in ASCII transcoded form such as standard HTML character references for multi-byte characters and hexadecimal value character pairs for binary bytes, but it is the responsibility of the application using the delivered Message to interpret transcoded sequences.

    Message Statistics

    A count of the total number of Messages successfully sent and received is maintained. Also a count of Messages dropped because they could not be sent or received for any reason is maintained. If a Message can not be sent or received due to a problem with the client communication channel the channel is closed and the Messenger employer is notified with a Done Message.

    @author Bradford Castalia, UA/PIRL @version 1.65 @see Message */ public class Messenger { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Messenger (1.65 2012/04/16 06:11:36)"; // Mesenger identification: /** The system time, in milliseconds since the start of the system epoch, when this Messanger was constructed. */ private long Start_Time = System.currentTimeMillis (); /** The {@link #DATE_FORMAT} pattern.

    The date format is:

    d MMMMM yyyy kk:mm:ss.SSS Z

    where d is the day number in the month (one or two digits), MMMMM is the full month name in the year, yyyy is the full year number, kk is the hour in the 24 hour day (two digits), mm is the minute in the hour (two digits), ss is the second in the minute (two digits), SSS is the millisecond in the second (three digits), and Z is the hours-minutes offset of the local time zone from the reference UTC zone. */ public static final String DATE_FORMATTING = "d MMMMM yyyy kk:mm:ss.SSS Z"; /** Date formatter used to represent the Messenger {@link #Start_Time() start time}.

    @see #DATE_FORMATTING */ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat (DATE_FORMATTING); // The address of this Messenger. private String Address; // The client's canonical hostname. private String Client_Hostname = null; /** The delimiter character separating the sections of a Messenger {@link #Address() address}. */ public static final char ADDRESS_SEGMENT_DELIMITER = ':'; /** An optional Message containing parameters used to {@link #Identity(Message) identify} the Messenger by employer applications.

    The content of the message is determined by the application. */ protected Message Identity = null; /** The name given to an {@link #Identity(Message) Identity Message}. */ public static final String IDENTITY_PARAMETER_NAME = Message.IDENTITY_ACTION; /** The name of the identity parameter that provides the name of the Messenger being identified. */ public static final String NAME_PARAMETER_NAME = Message.NAME_PARAMETER_NAME; /** {@link #Identity(Message) Identity} Message parameter set to contain the Messenger Address. */ public static final String ADDRESS_PARAMETER_NAME = "Address"; /** Message {@link #ACTION_PARAMETER_NAME} value sent in response to receiving an {@link #ADDRESS_PARAMETER_NAME} action Message for {@link #Deliver(Message) delivery}. */ public static final String MESSENGER_ADDRESS_PARAMETER_NAME = "Messenger_Address"; /** Message label parameter whose value is the message {@link Message#Route_To() route-to list}.

    @see Message#Packet_Label(int) */ public static final String ROUTE_TO_PARAMETER_NAME = Message.ROUTE_TO_PARAMETER_NAME; /** Message label parameter whose value is the message {@link Message#Route_From() route-from list}.

    @see Message#Packet_Label(int) */ public static final String ROUTE_FROM_PARAMETER_NAME = Message.ROUTE_FROM_PARAMETER_NAME; /** Message parameter specifying the action associated with a Message. */ public static final String ACTION_PARAMETER_NAME = Message.ACTION_PARAMETER_NAME; /** The {@link #Employer() employer} to whom Messages addressed to this Messenger are to be {@link Deliver(Message) delivered}. */ private Message_Delivered_Listener Employer = null; // Clients: /** The message communication SocketChannel to which messages will be sent and from which they will be received. */ private SocketChannel Client_Channel; /** A list of Messengers to whom Messages may be forwarded. */ private Vector Forwarding_Messengers = new Vector (); /** The name of the {@link #Forwarding_Messengers() forwarding Messengers} identities parameter group. */ public static final String FORWARDING_MESSENGERS_PARAMETER_NAME = "Forwarding_Messengers"; // Incoming message information: // Global ThreadGroup for Messengers. private static ThreadGroup Message_Listeners = new ThreadGroup ("Messengers"); private Thread Message_Listener = null; /** Flag indicating whether {@link #Listening_for_Messages()} should continue. */ private volatile boolean Listening_for_Messages = false; /** Maximum number of digits in the message label length value sequence.

    The message start synchronization sequence includes the length of the message label section as a decimal integer value. The text representation of the label length value has a maximum number of digits for a reasonable value. Seven digits should be more than sufficient for the largest reasonable label length values.

    @see #DEFAULT_BUFFER_INCREMENT */ public static final int MAX_LABEL_LENGTH_DIGITS = 7; /** The minimum size of the read buffer.

    The minimum read buffer size must allow for the Message start synchronization sequence:

    1. {@link Message#START_MARKER}
    2. {@link Message#START_MARKER_DELIMITER}
    3. Up to {@link #MAX_LABEL_LENGTH_DIGITS}
    4. {@link Message#START_MARKER_END}

    @see #DEFAULT_BUFFER_INCREMENT */ public static final int MINIMUM_BUFFER_SIZE = Message.START_MARKER.length () + MAX_LABEL_LENGTH_DIGITS + 2; /** The read buffer size increment.

    When additional read buffer capacity is needed for storing an incoming message the buffer size is increased by a multiple of the size increment to at least the required capacity. It follows that the initital read buffer size is the size increment.

    A reasonable buffer size increment will not be too small so that buffer expansion reallocation and data content copying will not need to happen too often; yet not so large that an excessive buffer size will result. It must not be smaller than the {@link #MINIMUM_BUFFER_SIZE}. The read buffer size will be increased as needed, but never decreased unless {@link #Shrink_Buffer()} is called. */ public static final int DEFAULT_BUFFER_INCREMENT = 1024; private static int Default_Buffer_Increment = DEFAULT_BUFFER_INCREMENT; private volatile int Buffer_Increment = Default_Buffer_Increment; /** The buffer used to store message content that is received.

    It might (only might) be more efficient to use a direct buffer which may optimize data transfers during socket input. However, for receive timeouts to be effective input must be done via the socket's InputStream which can not use a direct buffer, rather than via the SocketChannel itself; a temporary byte array must be allocated to hold the input which is then copied into the direct buffer. If a non-direct buffer is used its backing byte array can be provided to the InputStream for better efficiency.

    : The buffer is used as the read synchronization lock. This ensures that a Messenger will only read and deliver one message at a time so no other thread can cause message corruption. */ private ByteBuffer Read_Buffer = ByteBuffer.allocate (Buffer_Increment); // Message transmit and receive statistics. private long Messages_Received = 0, Message_Bytes_Received = 0, Messages_Received_Dropped = 0, Messages_Received_Corrupted = 0, Messages_Unforwardable = 0, Messages_Sent = 0, Message_Bytes_Sent = 0, Messages_Sent_Dropped = 0, Messages_Sent_Corrupted = 0; private int SEQUENTIAL_CORRUPTED_MESSAGES_LIMIT = 5; /** Bounced message due to unknown route-to address Message {@link #ACTION_PARAMETER_NAME} value. */ public static final String UNDELIVERABLE_ACTION = "Undeliverable"; /** Group of parameters containing the bounced message in an {@link #UNDELIVERABLE_ACTION} Message. */ public static final String ORIGINAL_MESSAGE_PARAMETER_NAME = "Original_Message"; /** A parameter containing an explanation description. */ public static final String EXPLANATION_PARAMETER_NAME = "Explanation"; /** A parameter containing an exception description. */ public static final String EXCEPTION_PARAMETER_NAME = "Exception"; /** The amount of time, in seconds, to wait for a server {@link #Connect(SocketAddress) connection}. */ public static final int DEFAULT_CONNECT_TIMEOUT = 10; private static int Connect_Timeout = DEFAULT_CONNECT_TIMEOUT; /** The maximum number of repeated server reconnect tries. */ public static final int MAX_RECONNECT_ATTEMPTS = 5; /** The amount of time, in seconds, to wait for closing a socket to complete. */ public static final int DEFAULT_CLOSE_TIME = 2; private Exception Error_Condition = null; private boolean Done_Delivered = false; private static String NL = System.getProperty ("line.separator"); // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_IDENTITY = 1 << 2, DEBUG_LISTENING = 1 << 3, DEBUG_SEND = 1 << 4, DEBUG_RECEIVE = 1 << 5, DEBUG_MESSAGE_START = 1 << 6, DEBUG_READ = 1 << 7, DEBUG_BUFFER = 1 << 8, DEBUG_ROUTING = 1 << 9, DEBUG_FORWARDING = 1 << 10, DEBUG_CONNECT = 1 << 11, DEBUG_CONFIGURE = 1 << 12, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Messenger on a client's communication channel connection.

    The {@link #Address() address} and {@link #Identity() identity} are intialized.

    @param employer The Message_Delivered_Listener to which messages will be delivered that are not automatically forwared. If null, messages can only be {@link #Receive(int) received} synchronously until an {@link #Employer(Message_Delivered_Listener) employer} is assigned. @param client_channel The SocketChannel to which messages will be sent and from which they will be received. @throws IllegalArgumentException If the client is null. */ public Messenger ( Message_Delivered_Listener employer, SocketChannel client_channel ) throws IllegalArgumentException { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Messenger Constructor:" + NL +" Client_Channel - " + client_channel); if ((Client_Channel = client_channel) == null) throw new IllegalArgumentException (ID + NL + "Unable to construct a Messenger with a null client SocketChannel."); Address = Host.IP_ADDRESS + ADDRESS_SEGMENT_DELIMITER + Port () + ADDRESS_SEGMENT_DELIMITER + Start_Time; if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Address - " + Address); if (employer != null) Employer (employer); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" Employer - " + employer); Identity (null); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<< Messenger Constructor: Address " + Address); } /** Construct a Messenger on a client's connection address.

    The client address is used to {@link #Connect(SocketAddress) connect} to the client's communication channel.

    The {@link #Address() address} and {@link #Identity() identity} are intialized.

    @param employer The Message_Delivered_Listener to which messages will be delivered that are not automatically forwared. If null, messages can only be {@link #Receive(int) received} synchronously until an {@link #Employer(Message_Delivered_Listener) employer} is assigned. @param client_address An InetSocketAddress to be used in establishing a communication channel with the client. @throws IllegalArgumentException If either the employer or server is null. @throws IOException If there was a problem connecting to the server. @see #Messenger(Message_Delivered_Listener, SocketChannel) */ public Messenger ( Message_Delivered_Listener employer, InetSocketAddress client_address ) throws IllegalArgumentException, IOException {this (employer, Connect (client_address));} /** Construct a Messenger to a client on a specified host and port.

    The hostname and port number define the address of the client where the communication channel connection is to be made.

    The {@link #Address() address} and {@link #Identity() identity} are intialized.

    @param employer The Message_Delivered_Listener to which messages will be delivered that are not automatically forwared. If null, messages can only be {@link #Receive(int) received} synchronously until an {@link #Employer(Message_Delivered_Listener) employer} is assigned. @param host The name or IP address of the host system where the server is running. @param port The port number on the server host system to use when establishing a connection. @throws IllegalArgumentException If either the employer or hostname is null or the port number is invalid. @throws IOException If there was a problem connecting to the server. @see #Messenger(Message_Delivered_Listener, InetSocketAddress) */ public Messenger ( Message_Delivered_Listener employer, String host, int port ) throws IllegalArgumentException, IOException {this (employer, new InetSocketAddress (host, port));} /** Construct a Messenger to a client on a specified host and port.

    The hostname and port number define the address of the client where the communication channel connection is to be made.

    The {@link #Address() address} and {@link #Identity() identity} are intialized.

    N.B.: Messages can only be {@link #Receive(int) received} synchronously until an {@link #Employer(Message_Delivered_Listener) employer} is assigned.

    @param host The name or IP address of the host system where the server is running. @param port The port number on the server host system to use when establishing a connection. @throws IllegalArgumentException If either the employer or hostname is null or the port number is invalid. @throws IOException If there was a problem connecting to the server. @see #Messenger(Message_Delivered_Listener, String, int) */ public Messenger ( String host, int port ) throws IllegalArgumentException, IOException {this (null, host, port);} private Messenger () {} /*============================================================================== Accessors */ /*------------------------------------------------------------------------------ Identification */ /** Get the address of this Messenger.

    The format of the address is:

    IP_ADDRESS:Port:Start_Time

    where IP_ADDRESS is the {@link Host#IP_Address() Internet Protocol address} of the host system, {@link #Port() Port} is the local port number used to communicate Messages, and {@link #Start_Time() Start_Time} is the time when this Messgener was constructed. This address is used to uniquely identify this Messenger when Messages are {@link #Send(Message) sent} and {@link #Receive(int) received}.

    @return The address String for this Messenger. @see Message#Route_To() @see Message#Route_From() */ public String Address () {return Address;} /** Get the time when this Messenger was constructed.

    The {@link System#currentTimeMillis() time} is obtained from the host system as the difference, measured in milliseconds, between the Messenger construction time and midnight, January 1, 1970 UTC. */ public long Start_Time () {return Start_Time;} /** Get the Message describing the identity of this Messenger.

    N.B.: A reference to, not a copy of, the identity Message is returned. This is done because the identity Message is not owned by the Messenger; it is controlled by the application.

    @return A Message containing parameters the identify this Messenger. This will be null if no {#Identity(Message) identity} has been assigned. */ public Message Identity () {return Identity;} /** Set the identity of this Messenger.

    The specified Message is copied, unless it is null in which case an empty message is provided.

    The identity Message may contain any parameters used by the employing application to identify this Messenger. The identity Message will be given the Name {@link #IDENTITY_PARAMETER_NAME} and it will be guaranteed to contain an {@link #ADDRESS_PARAMETER_NAME} with the {@link #Address() address} of this Messenger as its value. If the identity Message contains an {@link #ACTION_PARAMETER_NAME} parameter it is removed.

    @param identity A Message containing parameters that describe the identity of this Messenger. @return This Messenger. */ public Messenger Identity ( Message identity ) { if (identity == null) identity = new Message (); else { try {identity = new Message (identity);} catch (PVL_Exception exception) {/* Shouldn't happen */} } identity .Remove (ACTION_PARAMETER_NAME); identity .Set (ADDRESS_PARAMETER_NAME, Address) .Name (IDENTITY_PARAMETER_NAME); Identity = identity; if ((DEBUG & DEBUG_IDENTITY) != 0) System.out.println (">-< Messenger.Identity:" + NL + identity); return this; } /** Get a description of this Messenger.

    @return A String describing this Messenger. */ public String toString () { String string = ID + NL +" " + Host.FULL_HOSTNAME + ':' + Port () + ' ' + DATE_FORMAT.format (new Date (Start_Time)) + " (" + Address + ')' + NL +" Client_Channel: " + Client_Channel; return string; } /*------------------------------------------------------------------------------ Communication Channel */ /** Get the amount of time, in seconds, to wait for the communication channel to connect.

    @return The connection timeout, in seconds. @see #Connect_Timeout(int) */ public static int Connect_Timeout () {return Connect_Timeout;} /** Set the amount of time, in seconds, to wait for the communication channel to connect.

    @param timeout The connection timeout, in seconds. If less than one the {@link #DEFAULT_CONNECT_TIMEOUT} will be used. @see #Connect(SocketAddress) */ public static void Connect_Timeout ( int timeout ) { if (timeout < 1) timeout = DEFAULT_CONNECT_TIMEOUT; Connect_Timeout = timeout; } /** Get the connonical, fully qualified hostname for the remote host to which the Messenger communication channel is connected.

    @return The remote hostname String, or null if the Messenger is not connected. This may, of course, be the same as the local {@link Host#FULL_HOSTNAME}. */ public String Client_Hostname () { if (Client_Hostname == null) { InetAddress address = Client_Channel.socket ().getInetAddress (); if (address != null) Client_Hostname = address.getCanonicalHostName (); } return Client_Hostname; } /** Get the remote port number of the Message communication channel.

    N.B.: The availability of a remote port number does not imply that the communication channel {@link #Is_Connected() is connected}.

    @return The port number on the remote host system (which, of course, may be the same as the local host system) used for Message communication. This will be 0 if the Messenger is not yet connected. */ public int Client_Port () {return Client_Channel.socket ().getPort ();} /** Test if the Messenger's communication channel is connected to the client.

    A Messenger is connected if the {@link #Client_Channel() client communication channel} is open and connected.

    @return true if the client communication channel is connected; false otherwise. */ public boolean Is_Connected () { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">-< Messenger.Is_Connected: " + (Client_Channel.isOpen () && Client_Channel.isConnected ())); return Client_Channel.isOpen () && Client_Channel.isConnected (); } /** Get the client communication channel.

    @return The SocketChannel used for communication with the Messenger's client. @see #Is_Connected() */ public SocketChannel Client_Channel () {return Client_Channel;} /** Get the local port number used to communicate Messages.

    The port number is determined by the communication channel used to construct the Messenger. All Messages {@link #Receive(int) received} will be read from this port and all Messages {@link #Send(Message) sent} will be written to this port.

    @return The communication local port number. */ public int Port () {return Client_Channel.socket ().getLocalPort ();} /*------------------------------------------------------------------------------ Messages */ /** Get the Messenger employer.

    Messages recieved are {@link #Deliver(Message) delivered} to the employer if they are addressed to this Messanger or have no address.

    @return A Message_Delivered_Listener employer object. @see Message_Delivered_Listener @see #Employer(Message_Delivered_Listener) */ public Message_Delivered_Listener Employer () {return Employer;} /** Set the Messenger employer.

    Messages recieved are {@link #Deliver(Message) delivered} to the employer if they are addressed to this Messanger or have no address.

    @param employer A Message_Delivered_Listener employer object. @return This Messenger. @throws IllegalArgumentException If the employer is null. @see Message_Delivered_Listener */ public Messenger Employer ( Message_Delivered_Listener employer ) { if (employer == null) throw new IllegalArgumentException (ID + NL + "The Message_Delivered_Listener employer can not be null."); synchronized (Address) {Employer = employer;} return this; } /** Add a Messenger to the Message forwarding list.

    When a message is asynchronously received it is {@link #Route_Packet(Message, ByteBuffer) routed} based on its route-to address list. The list of forwarding Messengers provide routing possibilities in addition to delivery to the employer of this Messenger.

    @param messenger A Messenger. If null nothing is done. @return This Messenger. @see #Remove_Forwarding(Messenger) @see #Forwarding_Messenger(String) */ public Messenger Add_Forwarding ( Messenger messenger ) { if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println (">-< Messenger.Add_Forward:" + NL + messenger +" From -" + NL + this.toString ()); if (messenger != null) { synchronized (Forwarding_Messengers) { if (! Forwarding_Messengers.contains (messenger)) Forwarding_Messengers.add (messenger); } } return this; } /** Remove a Messenger from the Message forwarding list.

    @param messenger A Messenger. @return true if the Messenger was found in the list and removed; false otherwise. @see #Add_Forwarding(Messenger) */ public boolean Remove_Forwarding ( Messenger messenger ) { if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println (">-< Messenger.Remove_Forward:" + NL + messenger +" From -" + NL + this.toString ()); synchronized (Forwarding_Messengers) {return Forwarding_Messengers.remove (messenger);} } /** Get the Messenger associated with a forwarding address.

    @param address A Messenger (@link #Address() address}. If null null is returned. @return A Messenger, or null if the address does not match that of any Messenger in the {@link #Add_Forwarding(Messenger) forward Messenger list}. @see #Add_Forwarding(Messenger) */ public Messenger Forwarding_Messenger ( String address ) { if (address != null) { Messenger messenger = null; synchronized (Forwarding_Messengers) { int index = -1, total = Forwarding_Messengers.size (); while (++index < total) { messenger = Forwarding_Messengers.get (index); if (messenger.Address.equals (address)) return messenger; } } } return null; } /** Get the identities of all Messengers in the forwarding list.

    @return A {@link #FORWARDING_MESSENGERS_PARAMETER_NAME} Message containing the identities of all the Messengers in the forwarding list. This will be null if the forwarding list is empty. */ public Message Forwarding_Messengers () { Message message = null, identity; synchronized (Forwarding_Messengers) { if (! Forwarding_Messengers.isEmpty ()) { message = new Message (); message.Name (FORWARDING_MESSENGERS_PARAMETER_NAME); int index = -1, total = Forwarding_Messengers.size (); while (++index < total) { try { identity = new Message (Forwarding_Messengers.get (index).Identity ()); identity.Name (identity.Get (NAME_PARAMETER_NAME)); message.Add (identity); } catch (PVL_Exception exception) {} } } } return message; } /** Get the number of Messages successfully {@link #Send(Message) sent}.

    @return The number of Messages successfully sent. */ public long Messages_Sent () {return Messages_Sent;} /** Get the number of message bytes {@link #Send(Message) sent}.

    Message bytes include the message transmission {@link Message#Packet_Label(int) label} and the message body content.

    @return The total number of message packet bytes sent. */ public long Message_Bytes_Sent () {return Message_Bytes_Sent;} /** Get the number of Messages that could not be {@link #Send(Message) sent}.

    @return The number of Messages that could not be sent because the output was shutdown. */ public long Messages_Sent_Dropped () {return Messages_Sent_Dropped;} /** Get the number of Messages to be {@link #Send(Message) sent} that had content for which a PVL representation could not be generated.

    @return The number of Messages that could not be sent because the content was corrupted. */ public long Messages_Sent_Corrupted () {return Messages_Sent_Corrupted;} /** Get the number of Messages successfully {@link #Read_Packet(int) received}.

    @return The number of Messages successfully received. */ public long Messages_Received () {return Messages_Received;} /** Get the number of message bytes {@link #Read_Packet(int) received}.

    Message bytes include the message transmission {@link Message#Packet_Label(int) label} and the message body content.

    @return The total number of message packet bytes received. */ public long Message_Bytes_Received () {return Message_Bytes_Received;} /** Get the number of Messages that were dropped becasue they could not be completely {@link #Read_Packet(int) received}.

    @return The number of Messages that were dropped due to input shutdown before the message was completely received. */ public long Messages_Received_Dropped () {return Messages_Received_Dropped;} /** Get the number of Messages received that were corrupted in transmission.

    @return The number of Messages that were received but their PVL content could not be correctly parsed. */ public long Messages_Received_Corrupted () {return Messages_Received_Corrupted;} /** Get the number of Messages that could not be {@link #Forward(String, Message, ByteBuffer) forwarded}.

    @return The number of Messages that could not be forwarded due to an IOException. */ public long Messages_Unforwardable () {return Messages_Unforwardable;} /** Get the most recent error exception.

    @return The Exception that most recently occured during while attempting to {@link #Send(Message) send} or {@link #Receive(int) receive} a message. This will be null if no error condition has occurred since the Messenger was constructed or the last {@link #Reset_Error_Condition() reset} of the error condition. */ public Exception Error_Condition () {return Error_Condition;} /** Clear the most recent error condition.

    @return This Messenger. @see #Error_Condition() */ public Messenger Reset_Error_Condition () { Error_Condition = null; return this; } /*------------------------------------------------------------------------------ Read Buffer */ /** Set the default size to use for the read buffer increment.

    This value is only used when a Messenger is constructed and when a specified {@link #Buffer_Increment(int) read buffer increment} is to be set to the default value.

    @param amount The size, in bytes, of the default read buffer increment. @return The previous read buffer increment amount. @see #Buffer_Increment(int) */ public static int Default_Buffer_Increment ( int amount ) { int previous_amount = Default_Buffer_Increment; if (amount <= 0) amount = DEFAULT_BUFFER_INCREMENT; if (amount < MINIMUM_BUFFER_SIZE) amount = MINIMUM_BUFFER_SIZE; Default_Buffer_Increment = amount; return previous_amount; } /** Get the current default read buffer increment amount.

    @return The current default read buffer increment amount. */ public static int Default_Buffer_Increment () {return Default_Buffer_Increment;} /** Set the read buffer increment amount.

    The read buffer holds the message packet for a message that has been received until the message is delivered or forwarded.

    While a message packet is being read from the communication channel if the read buffer is found to have insufficient space for the message packet content being read the buffer capacity is increased by to the amount of space required rounded up to the next buffer increment amount.

    If a {@link #Shrink_Buffer() buffer shrink} is pending it will remain pending after the new buffer increment is set.

    @param amount The read buffer increment amount. This will not be allowed to be less than the {@link #MINIMUM_BUFFER_SIZE}. If the specified amount is less than or equal to zero the {@link #Default_Buffer_Increment() default buffer increment} is used. */ public Messenger Buffer_Increment ( int amount ) { if (amount <= 0) amount = Default_Buffer_Increment; if (amount < MINIMUM_BUFFER_SIZE) amount = MINIMUM_BUFFER_SIZE; if (Buffer_Increment < 0) // Shrink buffer flag. amount = -amount; Buffer_Increment = amount; return this; } /** Get the read buffer increment amount.

    @return The read buffer increment amount. */ public int Buffer_Increment () { int amount = Buffer_Increment; if (amount < 0) amount = -amount; return amount; } /** Get the capacity of the read buffer.

    @return The capacity of the read buffer. @see #Shrink_Buffer() */ public int Buffer_Capacity () {synchronized (Read_Buffer) {return Read_Buffer.capacity ();}} /** Shrink the read buffer size.

    When the next Message is read the size of the read buffer will be reduced to the minimum capacity to hold the Message. This capacity will be the Message label and content size rounded up to the next multiple of the {@link #Buffer_Increment(int) buffer size increment}. The buffer shrink will not be done again until this method is called again.

    N.B.: In general, the read buffer should be allowed to expand to the maximum size for messages to be read after which point the buffer will no longer need to be reallocated (a relatively expensive operation). However, if an extraordinarily large message has been read it might be beneficial to shrink the buffer to free the excess storage.

    @return This Messenger. @see #Buffer_Capacity() */ public Messenger Shrink_Buffer () { if (Buffer_Increment > 0) Buffer_Increment = -Buffer_Increment; return this; } /*============================================================================== Asynchrous Receiver */ /** Test if the Messenger thread is running and marked to continue {@link #Listen_for_Messages() listening for messages}.

    To only test if the Messenger thread is running use {@link Thread#isAlive()}.

    @return If true the Messenger thread is running and marked to continue listening for messages. If false the Messenger thread will stop listening for messages after the current Message is handled, or the next Message is handled if the Messenger thread is waiting for an incoming message. @see #Stop_Listening_for_Messages() */ public boolean Listening_for_Messages () {return Message_Listener != null;} /** Start the Messenger thread listening for messages.

    Asynchronous message receipt can only occur when this Messenger has an {@link #Employer(Message_Delivered_Listener) employer} to whom the messages will be delivered.

    If the message listener thread is not running and the Messenger is {@link #Is_Connected() connected} it will be started.

    @return The message listenter Thread. This will be null only if the Messenger is not connected. @throws IllegalStateException If the Messenger was constructed without an employer. @see #Listening_for_Messages() @see #Stop_Listening_for_Messages() */ public synchronized Thread Listen_for_Messages () { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println (">>> Messenger.Listen_for_Messages"); if (Employer == null) throw new IllegalStateException (toString () + NL + "Can't listen for messages without an employer that will receive them."); Thread message_listener = Message_Listener; if (message_listener == null && Is_Connected ()) { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println (" Starting a Message_Reader thread."); Message_Listener = message_listener = new Message_Reader (); Message_Listener.start (); } if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("<<< Messenger.Listen_for_Messages"); return message_listener; } /** Flag the Messenger thread to stop {@link #Listen_for_Messages() listening for messages}.

    The Messenger thread is flagged to stop listening for messages after the current Message is handled, or the next Message is handled if the Messenger thread is currently waiting for an incoming message.

    N.B.: The Messenger thread, if it is running, will not stop immediately. To wait for the Messenger thread to stop:

        messenger.Stop_Listening_for_Messages ();
        while (messenger.isAlive ())
            {
            try {messenger.join ())
            catch (InterruptedException e) {}
            }
    

    @return true if the Messenger has stopped listing for messages; false if the Messenger has not yet stopped. */ public synchronized boolean Stop_Listening_for_Messages () { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println (">-< Messenger.Stop_Listening_for_Messages: " + (Message_Listener == null)); Listening_for_Messages = false; return Message_Listener == null; } /** Get the group of all message listener threads.

    When a Messenger begins to asynchronously {@link #Listen_for_Messages() listen for messages} it runs a Thread which excecutes a loop that waits for a message packet to be {@link #Read_Packet(int) read} and then {@link #Route_Packet(Message, ByteBuffer) routes} the packet for delivery. The message listener thread will only stop on its own if a) after handling a message that has been read, the thread has been {@link #Stop_Listening_for_Messages() flagged to stop}; b) the communication input channel has been shutdown; or c) an unexpected exception occurs.

    Each message listener thread is placed in a ThreadGroup named "Messengers" that is used by all Messengers. The name of each message listener thread in the group is the Messenger's {@link #Address() address}.

    @return The ThreadGroup containing all the message listener threads in use by Messengers. */ public static ThreadGroup Message_Listeners () {return Message_Listeners;} // Message listener thread. private class Message_Reader extends Thread { public Message_Reader () {super (Message_Listeners, "Messenger " + Address);} public void run () { Receive_Messages (); Message_Listener = null; } } // End of Message_Reader class. /** Run the Messenger {@link #Listen_for_Messages() listening for messages} thread.

    This method will not return until the thread stops. The thread will stop if, after handling a message that has been read, the thread has been {@link #Stop_Listening_for_Messages() flagged to stop}. It will also stop if the communication input channel has been shutdown, or if an unexpected exception occurs. */ private void Receive_Messages () { if (! Is_Connected ()) { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println (" The client channel is closed."); return; } String report = null; Exception exception = null; int sequential_corrupted_messages = 0; Listening_for_Messages = true; while (Listening_for_Messages && ! Client_Channel.socket ().isInputShutdown ()) { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("==> Messenger: Listening for message ..."); synchronized (Read_Buffer) { try { Message packet = Read_Packet (0); if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("==> Messenger: Routing packet ..."); Route_Packet (packet, Read_Buffer.slice ()); } catch (PVL_Exception except) { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("!!! Messenger: Receive failed -" + NL + except); if (++sequential_corrupted_messages < SEQUENTIAL_CORRUPTED_MESSAGES_LIMIT) continue; report = String.valueOf (sequential_corrupted_messages) + " sequential corrupted messages while listening for messages."; exception = except; break; } catch (IOException except) { if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("!!! Messenger: Receive failed -" + NL + except); if (! (except instanceof EOFException)) report = "Receive failed while listening for messages"; exception = except; break; } catch (Exception except) { if ((DEBUG & DEBUG_LISTENING) != 0) { System.out.println ("!!! Messenger: Unexpected exception -" + NL + exception); exception.printStackTrace (System.out); } report = "Unexpected exception while listening for messages."; exception = except; break; } } sequential_corrupted_messages = 0; } if ((DEBUG & DEBUG_LISTENING) != 0) System.out.println ("==> Messenger: Stop listening for messages"); if (Listening_for_Messages) Done (report, exception); Listening_for_Messages = false; } /*============================================================================== I/O */ /*------------------------------------------------------------------------------ Send */ /** Send a Message over the communication channel.

    The message label routing information is {@link #Update_Send_Routing(Message) updated}. Then the Message is converted to a {@link Message#Packet() message packet} which is {@link #Send_Packet(Message, ByteBuffer) sent} to the output channel. After successfully sending the message the count of (@link #Messages_Sent() messages sent} is incremented and the count of {@link #Message_Bytes_Sent() message bytes sent} is updated.

    If the message can not be sent because the content can not be represented by PVL syntax the count of {@link #Messages_Sent_Corrupted() corrupted send messages } is incremented and the {@link #Error_Condition() error condition} is noted.

    @param message The Message to be sent. If null or empty, nothing is sent. @throws EOFException If the output communication channel was found to be shutdown before attempting to send the message. @throws IOException If there was a problem while writing the message to the communication channel. @throws PVL_Exception If the message content contains parameters that can not be represented by the Message PVL syntax. */ public void Send ( Message message ) throws IOException, PVL_Exception { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> Messenger.Send:" + NL + message.Routing () + NL + message); if (message == null || message.List_Size () == 0) { if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< Messenger.Send: No message"); return; } Update_Send_Routing (message); // Send the message content. try {Send_Packet (null, message.Packet ());} catch (PVL_Exception exception) { if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("!!! Message is invalid -" + NL + exception.getMessage ()); ++Messages_Sent_Corrupted; Error_Condition = exception; throw exception; } if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< Messenger.Send"); } /** Send the message packet.

    If the content is null or has nothing remaining, nothing is done.

    If the output channel has been shutdown the count of {@link #Messages_Sent_Dropped() send messages dropped} is incremented, the {@link #Error_Condition() error condition} is set to an EOFException, and the Messenger is put in the {@link #Done(String, Exception) done} state.

    If the message label is not null its routing information is {@link #Update_Send_Routing(Message) updated} and then converted to a {@link Message#Packet_Label(int) packet label String} which is wrapped in a ByteBuffer that is added to a message content buffers array. If the message label is null the packet content contains the entire message, including the message label, as provided by the {@link Message#Packet() message packet} generator.

    The content ByteBuffer is added to a message message content buffers array and the sequence of buffers is written to the output channel. This is done as a single operation, rather than writing the message label separately from the message body content, to ensure that the message will not be inadvertently interleaved with another message being written to the same ouput channel at the same time. The count of {@link #Messages_Sent() messages sent} is incremented and the count of {@link #Message_Bytes_Sent() message bytes sent} is updated.

    If the message packet can not be transmitted the count of {@link #Messages_Sent_Dropped() send messages dropped} is incremented, the {@link #Error_Condition() error condition} is set to the exception that occured, and the Messenger is put in the {@link #Done(String, Exception) done} state.

    N.B.: The message routing address lists are only updated if a message label is provided. Otherwise the content buffer is presumed to already contain the correct routing information.

    @param message_label A Message containing the message start synchronization sequence and the routing parameters. This may be null if the content contains the entire message including the message label. @param content A ByteBuffer containing the message packet content. If null or empty nothing will be done. @throws EOFException If the output communication channel was found to be shutdown before attempting to send the message. @throws IOException If there was a problem while writing the message to the communication channel. */ protected void Send_Packet ( Message message_label, ByteBuffer content ) throws IOException { if ((DEBUG & DEBUG_SEND) != 0) System.out.println (">>> Messenger.Send_Packet:"); if (content == null || ! content.hasRemaining ()) { if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< Messenger.Send_Packet: No message"); return; } if (Client_Channel.socket ().isOutputShutdown ()) { EOFException exception = new EOFException (toString () + NL + ((message_label == null) ? "" : (message_label.Routing () + NL)) + "Send can not be done because output is shutdown."); if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("!!! Message could not be sent -" + NL + exception.getMessage ()); ++Messages_Sent_Dropped; Error_Condition = exception; Done ("Unable to send message.", Error_Condition); throw exception; } // Data buffer(s) that will be transmitted. ByteBuffer[] content_buffers; if (message_label != null) { Update_Send_Routing (message_label); // Get the Message label in PVL form. String label = message_label.Packet_Label (content.remaining ()); if ((DEBUG & DEBUG_SEND) != 0) System.out.print (" Message label:" + NL + label); // Message label content. content_buffers = new ByteBuffer[2]; content_buffers[0] = ByteBuffer.wrap (label.getBytes ()); content_buffers[1] = content; } else { // Message body content. content_buffers = new ByteBuffer[1]; content_buffers[0] = content; } if ((DEBUG & DEBUG_SEND) != 0) System.out.println (" Message content:" + NL + Message.Content_String (content)); try { // Transmit the message content. Message_Bytes_Sent += Client_Channel.write (content_buffers); ++Messages_Sent; } catch (IOException exception) { exception = new IOException (toString () + NL + ((message_label == null) ? "" : (message_label.Routing () + NL)) + "Unable to send message." + exception); if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("!!! Message could not be sent -" + NL + exception); ++Messages_Sent_Dropped; Error_Condition = exception; Done ("Unable to send message.", Error_Condition); throw exception; } catch (Exception exception) { if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("!!! Message could not be sent -" + NL + exception); ++Messages_Sent_Dropped; Error_Condition = new IOException (toString () + NL + exception); Error_Condition.initCause (exception); Done ("Unable to send message.", Error_Condition); throw (IOException)Error_Condition; } if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("<<< Messenger.Send_Packet"); } /** Update the message send routing information of a Message.

    If the message is not null and {@link Message#To() the last address on its route-to list} is the same as this Messenger's {@link #Address() address} it is (@link Message#Pop_To() removed from the route-to list} and added to the {@link Message#From(String) route-from} list.

    N.B.: This method is only intended for use when sending a message. Message {@link #Deliver(Message) delivery} and synchronous {@link #Receive(int) receive} is effectively "sending" the message to the Messenger {@link #Employer() employer}.

    @param message The Message to have its routing information updated. If null, nothing is done. */ protected void Update_Send_Routing ( Message message ) { if (message != null) { if (Address.equals (message.To ())) // Remove this Messenger's address from the To list. message.Pop_To (); // Add this Messenger's address to the From list. message.From (Address); } } /*------------------------------------------------------------------------------ Receive */ /** Receive a message synchronously.

    If this Messenger is currently asynchronously listening for messages an IllegalStateException will be thrown. The Messenger must first be {@link #Stop_Listening_for_Messages() flagged to stop listening} and have actually stopped.

    A {@link #Read_Packet(int) message packet is read} from the input channel. Its routing information is {@link #Update_Send_Routing(Message) updated} and the packet content is used to set the {@link Message#Content(ByteBuffer) message content}.

    @param timeout The maximum amount of time, in seconds, to wait for message arrival. If less than or equal to zero the wait will be indefinate. @return The Message that was received. This will not be null. @throws IllegalStateException If this Messenger is currently asynchronously listening for messages. @throws IOException If a message could not be read. There are various possible forms of this exception:

    SocketTimeoutException
    The timeout expired.
    EOFException
    The input channel was shutdown or closed.
    Other
    Read error on the input channel.
    @throws PVL_Exception If the message content can not be parsed or is not valid. */ public Message Receive ( int timeout ) throws IllegalStateException, // This exception is passed through from the Read method. IOException, // This exception may be passed through from Read_Packet. PVL_Exception { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (">>> Messenger.Receive: " + timeout); if (timeout < 0) timeout = 0; if (Message_Listener != null) throw new IllegalStateException (toString () + NL + "Can not synchronously receive a message" + NL + "while asynchronously listening for messages."); Message message = null; PVL_Exception except = null; /* Get an exclusive lock on the Read_Buffer while it is being used. The Read_Buffer is reused so it is important that only one thread will access it for reading a message at any time. */ try { synchronized (Read_Buffer) { // Read the Message packet. message = Read_Packet (timeout); Update_Send_Routing (message); // Set the message content from the Read_Buffer. try {message.Content (Read_Buffer);} catch (PVL_Exception exception) { except = new PVL_Exception (toString () + NL + "The message content received is invalid." + exception.getMessage ()); if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println ("!!! Message is invalid -" + NL + except.getMessage ()); ++Messages_Received_Corrupted; Error_Condition = except; } } } catch (PVL_Exception exception) { // From Read_Packet. Done ("Receive message failed.", exception); throw exception; } catch (IOException exception) { Done ("Receive message failed.", exception); throw exception; } if (except != null) { Done ("Receive message failed.", except); throw except; } if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Message -" + NL + message + NL +"<<< Messenger.Receive"); return message; } /** Receive a message synchronously.

    This is the same as {@link #Receive(int) receiving a message} with no timeout.

    @return The Message that was received. This will not be null. @throws IllegalStateException If this Messenger is currently asynchronously listening for messages. @throws IOException If a message could not be read. @throws PVL_Exception If the message content can not be parsed or is not valid. */ public Message Receive () throws IllegalStateException, IOException, PVL_Exception {return Receive (0);} /** Read a Message packet from the communication channel.

    A Message packet is a tentative Message: A packet Message contains only the label section of the transmitted message; the message body content remains unparsed in the read buffer. The label section must start with the message start synchronization sequence:

    1. {@link Message#START_MARKER}
    2. {@link Message#START_MARKER_DELIMITER}
    3. Up to {@link #MAX_LABEL_LENGTH_DIGITS}
    4. {@link Message#START_MARKER_END}

    The input {@link #Client_Channel() communication channel} is read one byte/character at a time until the entire message start synchnronization sequence has been recognized. Any unexpected character value will cause the scan for the message start synchronization sequence to start again from the beginning. The digits in the start synchronization sequence represent the size of the message label section that follows. This many bytes are read from the input channel and used to construct a {@link Message#Message(ByteBuffer) message}. This message label has its {@link Message#Route_To(Value) route-to list} and {@link Message#Route_From(Value) route-from list} set from the {@link #ROUTE_TO_PARAMETER_NAME} Value and {@link #ROUTE_FROM_PARAMETER_NAME} Value, respectively, if they are present. The {@link Message#Content_Amount() content amount} of the message label is obtained and this many bytes are read from the input channel. The message is invalid if the content amount can not be obtained from the message label.

    Though the content section of the message will have been read, it remains unparsed in the Read_Buffer with its position set to the beginning of the content data and its limit set to the end of message data. If the message packet is to be forwarded there is no need to parse the message content section; otherwise the Receive method will replace the Message with the content section to generate the Message to be delivered.

    N.B.: If the label section can not be parsed the count of {@link #Messages_Received_Corrupted() corrupted messages} will be incremented and then it will not be possible to read the content section because its size is unknown. In this case message synchronization will creep over the remaining unread message to locate the beginning of the next message.

    The count of {@link #Messages_Received() messages received} is incremented when an entire Message has been received. The count of {@link #Message_Bytes_Received() message bytes received} is updated as they are read. Thus the number of bytes received includes {@link #Messages_Received_Corrupted() corrupted messages}.

    N.B.: It is the responsibility of the user to call the {@link #Done(String, Exception) Done} method if an exception is thrown. Done is not called here because this method is expected to be called within a block synchronized on the Read_Buffer and the Done method should not be called within this sychnronized block since the {@link #Deliver(Message) delivery} of the {@link Message#Done() Done Message} could result in an Employer's delivery Thread calling Done again which would result in a deadlock.

    @param timeout The amount of time (seconds) to wait for the arrival of the message before a timeout exception will occur. @return A Messsage with message label section parameters with the To and From data members having been set from these parameters. @throws IOException If there was a problem reading the message. @throws PVL_Exception If the label section could not be parsed as valid PVL syntax. */ protected Message Read_Packet ( // Passed on to the Read method. int timeout ) throws // This exception is passed through from the Read method. IOException, // This exception is passed through from the Message constructor. PVL_Exception { if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (">>> Messenger.Read_Packet: " + timeout); Message packet = null; // Read the message label section. Ensure_Read_Buffer_Capacity (Read_Message_Start (timeout)); Read (timeout); // Reset the Read_Buffer for parsing of the label. Read_Buffer.flip (); try { // Construct the label Message. packet = new Message (Read_Buffer); // Apply the routing values from the label. packet.Route_To (packet.Value_of (ROUTE_TO_PARAMETER_NAME)); packet.Route_From (packet.Value_of (ROUTE_FROM_PARAMETER_NAME)); // Move the Read_Buffer position forward to end-of-data. int content_position = Read_Buffer.limit (); Read_Buffer.position (content_position); // Read the message content section. Ensure_Read_Buffer_Capacity (packet.Content_Amount ()); if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Content amount = " + Read_Buffer.remaining ()); Read (timeout); // Reset the Read_Buffer position to the beginning of the content section. Read_Buffer.position (content_position); } catch (PVL_Exception exception) { exception = new PVL_Exception (toString () + NL + "Invalid message packet label." + NL + exception.getMessage ()); if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println ("!!! Message is invalid -" + NL + exception.getMessage ()); ++Messages_Received_Corrupted; Error_Condition = exception; throw exception; } if ((DEBUG & DEBUG_RECEIVE) != 0) System.out.println (" Message label -" + NL + packet + NL +"<<< Messenger.Read_Packet"); ++Messages_Received; return packet; } /** Read a message start synchronization sequence.

    See the {@link Read_Packet(int)} method for a description of the message start synchronization sequence.

    It is assumed that the input stream being read is unreliable. To find a message start synchronization sequence in the input stream one byte at a time is {@link Read(int)} into the Read_Buffer - the buffer limit is one more than the buffer position - and tested for being the expected character. If any byte read fails to provide the expected character the search for the synchronization sequence begins again with the last byte read.

    @param timeout The maximum time, in seconds, to wait for a byte to be read. @return The label size value obtained from the message start synchronization sequence @throws IOException If there was a problem reading a byte. */ private int Read_Message_Start ( int timeout ) // This exception is passed through from the Read method. throws IOException { if ((DEBUG & DEBUG_MESSAGE_START) != 0) System.out.println (">>> Messenger.Read_Message_Start: " + timeout); int index; char character; String message_length; // Prime the pump. Read_Buffer .position (0) .limit (1); Read (timeout); Read_until_Message: while (true) { // Read until the Message START_MARKER has been read. index = 0; while (true) { if ((DEBUG & DEBUG_MESSAGE_START) != 0) System.out.println (" Messenger.Read_Message_Start: " + "expecting '" + Message.START_MARKER.charAt (index) + "'"); if ((char)Read_Buffer.get (index) == Message.START_MARKER.charAt (index)) { if (++index == Message.START_MARKER.length ()) // START_MARKER has been read. break; // Read the next byte. Read_Buffer.limit (index + 1); Read (timeout); } else { // Start again. Read_Buffer.put (0, Read_Buffer.get (index)); Read_Buffer .position (1) .limit (1); index = 0; } } // Check for the parameter name delimiter. Read_Buffer.limit (Read_Buffer.limit () + 1); Read (timeout); if ((DEBUG & DEBUG_MESSAGE_START) != 0) System.out.println (" Messenger.Read_Message_Start: " + "expecting '" + Message.START_MARKER_DELIMITER + "'"); if ((char)Read_Buffer.get (Read_Buffer.position () - 1) != Message.START_MARKER_DELIMITER) { // Start all over again. Read_Buffer.put (0, Read_Buffer.get (Read_Buffer.position () - 1)); Read_Buffer .position (1) .limit (1); continue; } // Read digits until the statement end delimiter is seen. Read_Buffer.limit (Read_Buffer.limit () + 1); Read (timeout); message_length = ""; while (true) { character = (char)Read_Buffer.get (Read_Buffer.position () - 1); if ((DEBUG & DEBUG_MESSAGE_START) != 0) System.out.println (" Messenger.Read_Message_Start: expecting a digit or '" + Message.START_MARKER_END + "'"); if (character == Message.START_MARKER_END) // End of message start synchronization sequence. break Read_until_Message; if (character < '0' || character > '9' || message_length.length () == MAX_LABEL_LENGTH_DIGITS) { // Start all over again. Read_Buffer.put (0, (byte)character); Read_Buffer .position (1) .limit (1); continue Read_until_Message; } message_length += character; // Read the next byte. Read_Buffer.limit (Read_Buffer.limit () + 1); Read (timeout); } } if ((DEBUG & DEBUG_MESSAGE_START) != 0) System.out.println ("<<< Messenger.Read_Message_Start: " + message_length); return Integer.parseInt (message_length); } /** Read into the Read_Buffer.

    The Read_Buffer position and limit determine the number of bytes to read. This method only returns after successfully reading all required bytes; otherwise an IOException is thrown.

    @param timeout The maximum time, in seconds, to wait for a read to complete. @throws IOException If there is a problem with the Client_Channel. This includes a SocketTimeoutException that might occur if the Client_Channel socket had been set to timeout. It also includes an EOFException if socket input was shutdown or an EOF condition was encountered while attempting to read from it. */ private void Read ( int timeout ) throws IOException { if (! Read_Buffer.hasRemaining ()) // Only enter if data is to be read. return; if ((DEBUG & DEBUG_READ) != 0) System.out.println (">>> Messenger.Read: " + timeout + NL + Read_Buffer); if (Client_Channel.socket ().isInputShutdown ()) { EOFException exception = new EOFException (toString () + NL + "Read can not be done because input is shutdown."); if ((DEBUG & DEBUG_SEND) != 0) System.out.println ("!!! Read can not be done because input is shutdown -" + NL + exception.getMessage ()); if (Read_Buffer.position () > 0) ++Messages_Received_Dropped; Error_Condition = exception; throw exception; } IOException IO_exception = null; // DEBUG int start_position = Read_Buffer.position (), amount; try { if (timeout != 0) { byte[] byte_array; int offset; if (Read_Buffer.hasArray ()) { byte_array = Read_Buffer.array (); offset = Read_Buffer.arrayOffset () + Read_Buffer.position (); } else { byte_array = new byte[Read_Buffer.remaining ()]; offset = 0; } Client_Channel.socket ().setSoTimeout (timeout * 1000); while ((amount = Client_Channel.socket ().getInputStream ().read (byte_array, offset, Read_Buffer.remaining ())) >= 0) { if ((DEBUG & DEBUG_READ) != 0) { System.out.print (" Messenger.Read: read " + amount + " -" + NL + '>'); for (int index = offset; index < (offset + amount); index++) System.out.print ((char)byte_array[index]); System.out.println ("<"); } if (amount > 0) { Message_Bytes_Received += amount; if (Read_Buffer.hasArray ()) Read_Buffer.position (Read_Buffer.position () + amount); else Read_Buffer.put (byte_array, offset, amount); if (! Read_Buffer.hasRemaining ()) { if ((DEBUG & DEBUG_READ) != 0) System.out.println ("<<< Messenger.Read"); return; } } } } else { while (! Client_Channel.socket ().isInputShutdown () && (amount = Client_Channel.read (Read_Buffer)) >= 0) { if ((DEBUG & DEBUG_READ) != 0) { int end_position = Read_Buffer.position (); System.out.print (" Messenger.Read: read " + amount + " -" + NL + '>'); Read_Buffer.position (start_position); while (Read_Buffer.position () < end_position) System.out.print ((char)Read_Buffer.get ()); System.out.println ("<"); start_position = end_position; } Message_Bytes_Received += amount; if (! Read_Buffer.hasRemaining ()) { if ((DEBUG & DEBUG_READ) != 0) System.out.println ("<<< Messenger.Read"); return; } // Try again after a brief pause. try {Message_Listener.sleep (250);} catch (InterruptedException exception) {} } } } catch (IOException exception) { if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Messenger.Read: IOException -" + NL + exception); /* Hack! When the socket connection is reset by the peer this is equivalent to an EOF condition. */ if (exception.getMessage () != null && exception.getMessage ().indexOf ("Connection reset by peer") < 0) IO_exception = exception; } catch (Exception exception) { if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Messenger.Read: Exception -" + NL + exception); IO_exception = (IOException)new IOException (toString () + NL + exception) .initCause (exception); } String explanation = "Read did not complete." + NL; if (IO_exception == null) // Either input was shutdown or an EOF was encountered. IO_exception = new EOFException (toString () + NL + "End of input encountered."); else if (IO_exception instanceof AsynchronousCloseException) { explanation += "The communication channel was closed."; IO_exception = new EOFException (toString () + NL + explanation + NL + IO_exception); } else if (IO_exception instanceof SocketTimeoutException) { explanation += "Timeout after " + timeout + " second" + ((timeout == 1) ? "" : "s"); IO_exception = new SocketTimeoutException (toString () + NL + explanation); } else { explanation += IO_exception.toString (); IO_exception = (IOException)new IOException (toString () + NL + explanation) .initCause (IO_exception); } Error_Condition = IO_exception; if ((DEBUG & DEBUG_READ) != 0) System.out.println (" Messenger.Read: " + IO_exception); throw IO_exception; } /** Ensure that the Read_Buffer has sufficient storage space.

    If the space between the current Read_Buffer position and its capacity is less than the amount required a new buffer is allocated that will provide the required space rounded up to the next Buffer_Increment.

    The Read_Buffer limit, regardless of whether a new buffer was allocated or not, is set to the current Read_Buffer position plus the amount.

    @param amount The amount of storage space beyond the current position that is required. */ private void Ensure_Read_Buffer_Capacity ( int amount ) { if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (">>> Messenger.Ensure_Read_Buffer_Capacity: " + amount + NL +" " + Read_Buffer + NL +" Available = " + (Read_Buffer.capacity () - Read_Buffer.position ())); if (amount <= 0) throw new IllegalArgumentException (toString () + NL + "Invalid Ensure_Read_Buffer_Capacity amount: " + amount); // Required Read_Buffer limit. int limit = Read_Buffer.position () + amount, increment = Buffer_Increment; if (limit > Read_Buffer.capacity () || // Buffer shrink flag. increment < 0) { if (increment < 0) // Reset the buffer size increment. Buffer_Increment = increment = -increment; // Round up to the next buffer size increment. amount = increment * ((limit / increment) + (((limit % increment) == 0) ? 0 : 1)); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Allocated = " + amount); ByteBuffer buffer = ByteBuffer.allocate (amount); // Copy over the existing data. Read_Buffer.flip (); buffer.put (Read_Buffer); // Free the old buffer and assign the new one. Read_Buffer = null; System.gc (); Read_Buffer = buffer; } Read_Buffer.limit (limit); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" " + Read_Buffer + NL +"<<< Messenger.Ensure_Read_Buffer_Capacity"); } /*------------------------------------------------------------------------------ Routing */ /** Route a message packet to a recipient.

    The packet's {@link Message#To() next route-to address} examined. If there is an address (the route-to list was not empty) and the address matches the {@link #Address() address of this Messenger} then the {@link Message#To() next address on the packet's route-to list}, if there is one, is compared with the addresses of the {@link #Forwarding_Messenger(String) forwarding Messengers list}. If the address is for a forwarding Messenger it is used to {@link #Send_Packet(Message, ByteBuffer) send the packet}.

    If the address removed from the route-to list is not for this Messenger (an empty route-to list is implicitly addressed to this Messenger) or a forwarding address is found that does not match the address of the forwarding Messenger, the message is bounced back to this Messenger's client. The bounce message contains an {@link #UNDELIVERABLE_ACTION} {@link Message#Action() action parameter}, an {@link #EXPLANATION_PARAMETER_NAME}, {@link #ROUTE_TO_PARAMETER_NAME} and {@link #ROUTE_FROM_PARAMETER_NAME} parameters {@link Message#Set(String, Object) set} to the address routing of the undeliverable Message, and the message that was received as an {@link #ORIGINAL_MESSAGE_PARAMETER_NAME}. If message forwarding failed an attempt will also be made to bounce the message back to this Messenger's client; an {@link #EXCEPTION_PARAMETER_NAME} is included in this case that provides the detail message from the exception that occurred.

    If the packet is neither forwarded nor bounced the Message {@link Message#Content(ByteBuffer) contains} is {@link #Deliver(Message) delivered} to this Messenger's {@link #Employer() employer}.

    If the content could not be parsed as valid PVL the {@link #Messages_Received_Corrupted() corrupted messages count} is incremented and the {@link #Error_Condition() error condition} is noted.

    @param message A received Message label. If null nothing is done. @param content A ByteBuffer holding the message content. @throws PVL_Exception If the content could not be parsed for Message delivery. */ protected void Route_Packet ( Message message, ByteBuffer content ) throws PVL_Exception { if (message == null) return; if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (">>> Messenger.Route_Packet:" + NL + message.Routing () + NL + message); // Check the address on the end of the To list. String address = message.To (); while (address != null && address.equals (Address)) { /* The Message is addressed to this Messenger. The message will either be delivered to the employer or forwarded to another Messenger, but in either case the address for this Messenger is to be removed from the end of the To list and the next address examined. */ message.Pop_To (); address = message.To (); } if (address != null) { // The address is for another Messenger. Forward (address, message, content); if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println ("<<< Message.Route_Packet"); return; } // Set the message content and deliver. try {Deliver (message.Content (content));} catch (PVL_Exception exception) { exception = new PVL_Exception (toString () + NL + message.Routing () + NL + "Unable to route message." + NL + exception.getMessage ()); if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println ("!!! Message is invalid -" + NL + exception.getMessage ()); ++Messages_Received_Corrupted; Error_Condition = exception; throw exception; } if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (" Packet message content -" + NL + message + NL +"<<< Messenger.Route_Packet"); } /** Forward a message packet.

    The address of this Messenger is added to the route-from list and the message packet is sent using the forwarding Messenger. If an IOException occurs message forwarding is disabled, the {@link #Messages_Unforwardable() unforwardable messages counter} is incremented and the {@link #Error_Condition() error condition} is set to the exception that occurred; the exception is not thrown since this will be done by the forwarding Messenger.

    @param message_label The packet label Message. @param content A ByteBuffer containing the message body content. */ private void Forward ( String address, Message message_label, ByteBuffer content ) { if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println (">>> Message.Forward: to " + address + NL + message_label.Routing () + NL + message_label); Messenger messenger = Forwarding_Messenger (address); if (messenger == null) { // Addressee unknown. if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println ("!!! Addressee unknown"); // Bounce. try {Send (Message .Action (UNDELIVERABLE_ACTION) .Set (EXPLANATION_PARAMETER_NAME, "Addressee unknown") .Set (ROUTE_TO_PARAMETER_NAME, message_label.Route_To ()) .Set (ROUTE_FROM_PARAMETER_NAME, message_label.Route_From ()) .Add (ORIGINAL_MESSAGE_PARAMETER_NAME, new Message (content)) .Reply_To (message_label));} catch (Exception except) {/* Junk mail */} ++Messages_Unforwardable; if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println (" Messages_Unforwardable = " + Messages_Unforwardable + NL +"<<< Message.Forward"); return; } // Put this Messenger's address on the From list. message_label.From (Address); // Forward the packet for routing. try {messenger.Send_Packet (message_label, content);} catch (IOException exception) { /* DO NOT THROW THIS EXCEPTION! The Forwarding_Messenger will take care of its exception. It is sufficient that this Messenger removes the forwarding messenger that is no longer output capable. */ if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println ("!!! Message could not be forwarded -" + NL + exception.getMessage ()); // Bounce. try {Send (Message .Action (UNDELIVERABLE_ACTION) .Set (EXPLANATION_PARAMETER_NAME, "Message could not be forwarded to " + messenger.Address ()) .Set (EXCEPTION_PARAMETER_NAME, exception.toString ()) .Set (ROUTE_TO_PARAMETER_NAME, message_label.Route_To ()) .Set (ROUTE_FROM_PARAMETER_NAME, message_label.Route_From ()) .Add (ORIGINAL_MESSAGE_PARAMETER_NAME, new Message (content)) .Reply_To (message_label));} catch (Exception except) {/* Junk mail */} Remove_Forwarding (messenger); ++Messages_Unforwardable; if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println (" Messages_Unforwardable = " + Messages_Unforwardable); Error_Condition = exception; } if ((DEBUG & DEBUG_FORWARDING) != 0) System.out.println ("<<< Message.Forward"); } /** Deliver a Message locally.

    If the Message contains an {@link #ADDRESS_PARAMETER_NAME} {@link #ACTION_PARAMETER_NAME} value it is changed to {@link #MESSENGER_ADDRESS_PARAMETER_NAME} and sent as a {@link Message#Reply_To(Message) reply to} the source of the Message. In effect this special Message is delivered to the Messenger which simply replies so the sender can it can obtain the address routing information for this Messenger.

    If the Messenger has an {@link #Employer() employer} all non-Address Messages are delivered to it. The {@link #Address() address} of this Messenger is {@link Message#From(String) added to the Message's route-from list} and the {@link Message#Route_To() route-to list} is emptied. The employer's {@link Message_Delivered_Listener#Message_Delivered Message_Delivered method} is invoked with a {@link Message_Delivered_Event} containing the Message obtained by this Messenger. N.B.: The delivery of Message is synchronized on the employer to prevent it changing while the during delivery; do not attempt changing the employer in the thread that receives the Message delivery or a deadlock will occur.

    N.B.: If the Messenger has been constructed without an employer, and one has not been assigned, then obviously no Message can be delivered.

    @param message The Message to be delivered to the employer. If null, nothing is done. */ protected void Deliver ( Message message ) { if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (">>> Messenger.Deliver:" + NL + message); if (message == null) { if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (" No Message" + NL +"<<< Messenger.Deliver"); return; } if (ADDRESS_PARAMETER_NAME.equals (message.Action ())) { try { Send (message .Set (ACTION_PARAMETER_NAME, MESSENGER_ADDRESS_PARAMETER_NAME) .Reply_To (message)); if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (" " + MESSENGER_ADDRESS_PARAMETER_NAME + " reply sent"); } catch (Exception exception) {/* Lost Message */} if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println ("<<< Messenger.Deliver"); return; } synchronized (Address) // Prevent employer change during delivery. { if (Employer == null) { if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println (" No Employer" + NL +"<<< Messenger.Deliver"); return; } // Clear the To list. message.Clear_Route_To (); // Add the address of this Messenger to the From list. message.From (Address); // Deliver the message to the employer. Employer.Message_Delivered (new Message_Delivered_Event (this, message)); } if ((DEBUG & DEBUG_ROUTING) != 0) System.out.println ("<<< Messenger.Deliver"); } /** Force the Messenger to close its client communication channel.

    Input and output for the socket of the {@link #Client_Channel() client communication channel} is shutdown if it not already done. The channel is then closed if it was open.

    N.B.: No {@link Message#Done() Done} Message notification is delivered to the {@link #Employer() employer}.

    @return true if the Messenger was not done when the method was called (this call caused the communication channel I/O to be shutdown and/or closed); false if the communication channel had already been shutdown and closed for any reason. @see #Done(String, Exception) */ public boolean Done () { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> Messenger.Done" + NL + this.toString ()); Listening_for_Messages = false; boolean done = false; synchronized (Client_Channel) { if (! Client_Channel.socket ().isInputShutdown ()) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Shutting down input"); try {Client_Channel.socket ().shutdownInput ();} catch (IOException exception) {/* Nothing for it */} done = true; } if (! Client_Channel.socket ().isOutputShutdown ()) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Shutting down output"); try {Client_Channel.socket ().shutdownOutput ();} catch (IOException exception) {/* Nothing for it */} done = true; } if (Client_Channel.isOpen ()) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Closing the Client_Channel connection"); try {Client_Channel.close ();} catch (IOException exception) {/* Sigh */} done = true; } } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (this.toString () +"<<< Messenger.Done: " + done); return done; } /** Deliver a {@link Message#Done() Done} Message to the {@link #Employer() employer} and close its communication channel.

    N.B.: The Done Message is delivered before the request to close the communication channel occurs; the communicaiton channel may already have been closed by an external event, however.

    N.B.: Only one Done Message will ever be delivered by each Messenger object.

    @param explanation If non-null an {@link #EXPLANATION_PARAMETER_NAME} is included in the Done Message with the explanation text as its value. @param exception If non-null an {@link #EXCEPTION_PARAMETER_NAME} is included in the Done Message that provides the exception detail message as its value. @see #Done() @see #Is_Connected() */ public void Done ( String explanation, Exception exception ) { if (Done_Delivered) return; Message message = Message.Done (); if (exception != null) { if (exception instanceof IOException) { String report = exception.toString (); Throwable cause = exception.getCause (); while (cause != null) { report += NL + cause; cause = cause.getCause (); } message.Set (EXCEPTION_PARAMETER_NAME, report); } else { StringWriter report = new StringWriter (); exception.printStackTrace (new PrintWriter (report, true)); message.Set (EXCEPTION_PARAMETER_NAME, report.toString ()); } } if (explanation == null || explanation.length () == 0) explanation = "Communication with the client is done."; message.Set (EXPLANATION_PARAMETER_NAME, explanation); // Prevent recursive Done operations from the message recipient. Done_Delivered = true; Deliver (message); // Shutdown client I/O. Done (); } /** Force the Messenger to close its communication channel and deliver a {@link Message#Done() Done} Message.

    @param explanation If non-null an {@link #EXPLANATION_PARAMETER_NAME} is included in the Done Message with the explanation text as its value. @see #Done(String, Exception) */ public void Done ( String explanation ) {Done (explanation, null);} /*============================================================================== Helper Functions */ /** Connect to a SocketAddress.

    A new SocketChannel is opened and its socket set to SO_REUSEADDR mode (which allows the socket to be bound to the address port even if it is currently in a closed connection timeout state). An attempt is then made using the socket to connect to the specified address with a connection timeout of {@link #Connect_Timeout()} seconds. If the connection fails for any reason the SocketChannel is closed and the connection attempt is repeated. After {@link #MAX_RECONNECT_ATTEMPTS} a failure to connect will throw the last exception that cause the connection to fail.

    @param address The SocketAddress to which to connect. @return A connected SocketChannel. @throws IllegalArgumentException If the address is null. @throws IOException If there was a problem establishing the connection. */ public static SocketChannel Connect ( SocketAddress address ) throws IOException, IllegalArgumentException { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> Messenger.Connect"); if (address == null) throw new IllegalArgumentException (Messenger.ID + NL +"Unable to connect to a null address."); SocketChannel channel = null; int attempt = 0; while (attempt++ < MAX_RECONNECT_ATTEMPTS) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Attempt " + attempt); // Close the current socket. if (channel != null) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Closing the current connection"); try {channel.close ();} catch (IOException exception) {} channel = null; } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" ServerChannel."); channel = SocketChannel.open (); try {Configure_Socket (channel.socket ());} catch (SocketException exception) { throw new SocketException (ID + NL + "Unable to configure the communication socket." + NL + exception); } try { channel.socket ().connect (address, Connect_Timeout * 1000); break; } catch (SocketTimeoutException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Socket connect timeout."); String message = exception.getMessage (); if (message == null) message = exception.toString (); throw new SocketTimeoutException (Messenger.ID + NL + "Unable to connect to the server at " + address + NL + "after waiting " + Connect_Timeout + " seconds." + NL + message); } catch (IOException exception) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" IOException: "+ exception); if (attempt == MAX_RECONNECT_ATTEMPTS) { String message = exception.getMessage (); if (message == null) message = exception.toString (); message = Messenger.ID + NL + "Unable to connect to the server at " + address + NL + message; if (exception instanceof ConnectException) throw new ConnectException (message); else throw new IOException (message); } } } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Messenger.Connect"); return channel; } /** Configures a Socket.

    The socket options are set to:

    • Performance preferences with short connection time having top priority, followed by low latency priority over bandwidth.

      These priorities are a hint that may have no effect on the specific socket implementation being used by the system.

    • SO_REUSEADDR enabled.

      Allows a socket to be bound even though a previous connection is in a timeout state.

    • SO_KEEPALIVE enabled.

      Provide a persistent socket connection even if there is no application communication for an extended time.

    • SO_LINGER enabled with a linger time of {@link #DEFAULT_CLOSE_TIME} seconds.

      When the socket is closed and unsent data is queued for transmission the close will block until the data has been trasmitted or the linger time has been reached.

    N.B.: The socket must be configured before it is conneected for the socket options to be effective.

    @param socket The socket to be configured. */ public static void Configure_Socket ( Socket socket ) throws IOException { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> Messenger.Configure_Socket:" + NL +" Performance preferences - " + "connect time 2, latency 1, bandwith 0" + NL +" Reuse address - " + socket.getReuseAddress () + " -> true" + NL +" Keep alive - " + socket.getKeepAlive () + " -> true" + NL +" Linger - " + socket.getSoLinger () + " -> true, " + DEFAULT_CLOSE_TIME + NL +" Input buffer - " + socket.getReceiveBufferSize () + NL +" Output buffer - " + socket.getSendBufferSize () + NL +" No delay - " + socket.getTcpNoDelay () + NL +" Traffic class - " + socket.getTrafficClass () ); socket.setPerformancePreferences (2, 1, 0); socket.setReuseAddress (true); socket.setKeepAlive (true); socket.setSoLinger (true, DEFAULT_CLOSE_TIME); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Messenger.Configure_Socket"); } } pirl-2.3.8/PIRL/Messenger/tests/0000755000175000017500000000000012052546521016206 5ustar mathieumathieupirl-2.3.8/PIRL/Messenger/tests/Makefile0000644000175000017500000000036011057117367017653 0ustar mathieumathieu# Makefile for Java classes JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = test_Message.class all: ${CLASSES} classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Messenger/tests/test_Message.java0000644000175000017500000000501411742734030021473 0ustar mathieumathieu/* test_Message PIRL CVS ID: test_Message.java,v 1.3 2012/04/16 06:11:36 castalia Exp Unit test for the PIRL.Messenger.Message class. Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Messenger.*; import PIRL.PVL.*; import PIRL.Utilities.Checker; import java.nio.ByteBuffer; public class test_Message { public static void main ( String[] arguments ) { System.out.println ("*** test_Message:\n" +" " + Message.ID); Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; int exit_status = 0; String parameters_string = "Server = PIRL\n" + "GROUP = PIRL\n" + " Type = MySQL\n" + " Host = localhost\n" + "END_GROUP\n" + "END"; try { if (checker.Verbose) System.out.println ("--- Constructing an empty Message"); Message message = new Message (); if (checker.Verbose) System.out.println ("--- parameters_string:\n" + parameters_string); if (checker.Verbose) System.out.println ("--- Content (ByteBuffer.wrap (parameters_string.getBytes ()))"); message.Content (ByteBuffer.wrap (parameters_string.getBytes ())); checker.Check ("Content set from ByteBuffer", parameters_string, message.Description ()); String name = ".yp.*", expected = "MySQL"; checker.Check ("Matching (\"" + name + "\")", expected, message.Matching (name)); } catch (Exception exception) { System.out.println ("\n" +"Exception: " + exception + '\n' +" " + exception.getMessage ()); exit_status = 1; } System.out.println ("\n" +"Checks: " + checker.Checks_Total + '\n' +"Passed: " + checker.Checks_Passed); System.exit (exit_status); } } pirl-2.3.8/PIRL/Messenger/Dispatcher.java0000644000175000017500000025671511742734030020014 0ustar mathieumathieu/* Dispatcher PIRL CVS ID: Dispatcher.java,v 1.37 2012/04/16 06:11:36 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Messenger; import PIRL.PVL.*; import PIRL.Configuration.*; import PIRL.Utilities.Host; import PIRL.Utilities.Authentication; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.AsynchronousCloseException; import java.net.InetSocketAddress; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.DatagramPacket; import java.io.File; import java.io.PrintStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.security.KeyPair; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Collections; import java.util.Vector; /** A Dispatcher is a Messenger manager for communication between client processes.

    A Dispatcher is typically used as a background daemon application process that accepts connections from clients on a designated communications ports, allocates a Messenger to each client, manages the Messengers, and handles a core set of Messenger management Messages. When a Distpatcher is started it will send a "Hello" broadcast on a designated multicast port to any clients listening on the local host system who might be interested in making a connection.

    A Dispatcher may be subclassed to add a additional Message protocol support

    A Configuration file is used to Configure a Dispatcher's operating variables before it begins running.

    A log file may be specified where client connection/deconnection and other important events are reported.

    Support is provided for authenticated client connections using a public key to exchange an encoded private password that is decoded by a private key paired with the public key.

    @author Bradford Castalia - UA/PIRL @version 1.37 @see Messenger */ public class Dispatcher implements Message_Delivered_Listener, Runnable { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Messenger.Dispatcher (1.37 2012/04/16 06:11:36)"; /** The ID of of the Dispatcher.

    It is initialized to the {@link #ID}. Subclasses are expected to set this to their own ID. */ protected String Dispatcher_ID = ID; /** The default name of the Dispatcher: "Dispatcher". */ public static final String DEFAULT_DISPATCHER_NAME = "Dispatcher"; private static String Default_Dispatcher_Name = DEFAULT_DISPATCHER_NAME; private String Dispatcher_Name = null; private Message Identity = null; private Exception Run_Exception = null; // Configuration: /** The Dispatcher Configuration. */ protected Configuration The_Configuration = null; /** The configuration parameter name for the communicaitons port number value. */ public static final String PORT_PARAMETER_NAME = "Port"; /** The configuration parameter name for the maximum wait time, in seconds, for a new Messenger client connection to respond to the identify message. */ public static final String IDENTIFY_TIMEOUT_PARAMETER_NAME = "Identify_Timeout"; /** The configuration parameter name for the authentication password. */ public static final String PASSWORD_PARAMETER_NAME = "Password"; /** The configuration parameter name for the log file pathname. */ public static final String LOG_PATHNAME_PARAMETER_NAME = "Log_Pathname"; /** The configuration parameter name for the Hello port. */ public static final String HELLO_PORT_PARAMETER_NAME = "Hello_Port"; /** The configuration parameter name for the Hello multicast address. */ public static final String HELLO_ADDRESS_PARAMETER_NAME = "Hello_Address"; /** The configuration parameter name for the Hello repeat rate (seconds). */ public static final String HELLO_RATE_PARAMETER_NAME = "Hello_Repeat_Rate"; // Communications port: /** The default communications port number. */ public static final int DEFAULT_PORT = 4144; private int Port = 0; private ServerSocketChannel Listener; /** The default port on which to send the Hello datagram. */ public static final int DEFAULT_HELLO_PORT = 4170; private int Hello_Port; /** The default private multicast address to use in the Hello datagram. */ public static final String DEFAULT_HELLO_ADDRESS = "230.1.2.3"; private String Hello_Address; /** The content of the Hello broadcast datagram message. */ public static final String HELLO_MESSAGE = "Hello"; private static final int HELLO_COUNT = 12, HELLO_INTERVAL = 5; private Say_Hello Hello = null; // Messenger clients: /** The default maximum amount of time, in seconds, to wait for a new Messenger client to respond to an identify message. */ public static final int DEFAULT_IDENTIFY_TIMEOUT = 15; /** The minimum Messenger client identify timeout in seconds.

    @see #DEFAULT_IDENTIFY_TIMEOUT */ public static final int MINIMUM_IDENTIFY_TIMEOUT = 5; private volatile int Identify_Timeout = DEFAULT_IDENTIFY_TIMEOUT; // Authentication. /** Controls whether or not annonymous connections are allowed. */ public static final boolean UNAUTHENTICATED_CONNECTIONS_ALLOWED = false; private String Password = null, Public_Key = null; private KeyPair Keys = null; /** List of connected Messenger clients. */ protected Vector Messengers = new Vector (); /** List of Messenger clients that have requested connection notifications. */ protected Vector Report_to_Messengers = new Vector (); /** Automatically send connection notifications. */ protected volatile boolean Report_Messenger_Connections = true; /** Messenger management Message Actions and their parameters. */ public static final String ACTION_PARAMETER_NAME = Message.ACTION_PARAMETER_NAME, IDENTIFY_ACTION = Message.IDENTIFY_ACTION, KEY_PARAMETER_NAME = "Key", IDENTITY_ACTION = Message.IDENTITY_ACTION, NAME_PARAMETER_NAME = Message.NAME_PARAMETER_NAME, ADDRESS_PARAMETER_NAME = Messenger.ADDRESS_PARAMETER_NAME, ROUTE_TO_PARAMETER_NAME = Message.ROUTE_TO_PARAMETER_NAME, CLASS_ID_PARAMETER_NAME = Message.CLASS_ID_PARAMETER_NAME, MESSENGERS_REPORT_ACTION = "Messengers_Report", START_MESSENGER_REPORTING_ACTION= "Start_Messenger_Reporting", STOP_MESSENGER_REPORTING_ACTION = "Stop_Messenger_Reporting", LINK_MESSENGER_ACTION = "Link_to_Messenger", UNLINK_MESSENGER_ACTION = "Unlink_from_Messenger", STATUS_REPORT_ACTION = "Status_Report", CONFIGURATION_SOURCE_PARAMETER_NAME = "Configuration_Source", MESSENGER_STATUS_PARAMETER_NAME = "Messenger_Status", MESSAGES_SENT_PARAMETER_NAME = "Messages_Sent", MESSAGE_BYTES_SENT_PARAMETER_NAME = "Message_Bytes_Sent", MESSAGES_SENT_DROPPED_PARAMETER_NAME = "Messages_Sent_Dropped", MESSAGES_SENT_CORRUPTED_PARAMETER_NAME = "Messages_Sent_Corrupted", MESSAGES_RECEIVED_PARAMETER_NAME = "Messages_Received", MESSAGE_BYTES_RECEIVED_PARAMETER_NAME = "Message_Bytes_Received", MESSAGES_RECEIVED_DROPPED_PARAMETER_NAME = "Messages_Received_Dropped", MESSAGES_RECEIVED_CORRUPTED_PARAMETER_NAME = "Messages_Received_Corrupted", MESSAGES_UNFORWARDABLE_PARAMETER_NAME = "Messages_Unforwardable", MEMORY_STATUS_PARAMETER_NAME = "Memory_Status", MEMORY_AVAILABLE_PARAMETER_NAME = "Available_Memory", MEMORY_ALLOCATED_PARAMETER_NAME = "Allocated_Memory", MEMORY_FREE_PARAMETER_NAME = "Free_Memory", UNDELIVERABLE_ACTION = Messenger.UNDELIVERABLE_ACTION, ACK_ACTION = Message.ACK_ACTION, NACK_ACTION = Message.NACK_ACTION, ORIGINAL_MESSAGE_PARAMETER_NAME = Messenger.ORIGINAL_MESSAGE_PARAMETER_NAME, DONE_ACTION = Message.DONE_ACTION, EXPLANATION_PARAMETER_NAME = Messenger.EXPLANATION_PARAMETER_NAME, EXCEPTION_PARAMETER_NAME = Messenger.EXCEPTION_PARAMETER_NAME; // Logging: private boolean Logging = false; private String Log_Pathname = Default_Log_Filename (); /** Log "filename" for logging to stdout.

    @see #Log_Pathname(String) */ public static final String STDOUT = "-"; /** Log output stream. */ protected PrintStream Log_Stream = null; /** Formatting pattern for log message timestamps.

    @see #DATE_FORMAT */ public static final String DATE_FORMATTING = "d MMMMM yyyy kk:mm:ss.SSS Z"; /** Format for log message timestamps.

    @see #DATE_FORMATTING */ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat (DATE_FORMATTING); /** Log file marker written when a Dispatcher is started and after it has shutdown. */ public static final String START_STOP_MARKER = "=================================================="; // Miscellaneous: // The Java virtual machine (JVM) runtime environment (JRE) object. private static final Runtime JRE = Runtime.getRuntime (); /** Text new-line sequence. */ public static final String NL = System.getProperty ("line.separator"); /** Success exit status. */ public static final int EXIT_SUCCESS = 0; /** Command line syntax problem exit status. */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** The log file could not be opened for writing exit status. */ public static final int EXIT_CONFIG_FILE_PROBLEM = 2; /** The log file could not be opened for writing exit status. */ public static final int EXIT_NO_LOG_FILE = 3; /** IOException failure exit status. */ public static final int EXIT_IO_ERROR = 4; /** SecurityException exit status. */ public static final int EXIT_SECURITY_VIOLATION = 5; /** Unknown exception (possible programming flaw). */ public static final int EXIT_UNKNOWN_EXCEPTION = 6; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_BIND = 1 << 3, DEBUG_CONNECT = 1 << 4, DEBUG_AUTHENTICATE = 1 << 5, DEBUG_MESSAGES = 1 << 6, DEBUG_MESSENGERS = 1 << 7, DEBUG_IDENTITIES = 1 << 8, DEBUG_START_CONDUCTOR = 1 << 9, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a named Dispatcher with a configuration and a specified communication port.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    @param name The {@link #Dispatcher_Name(String) Dispatcher name}. @param configuration The Configuration to use to {@link #Configure(Configuration) configure} the Dispatcher when it is {@link #Start() started}. @param port The communications port number. If less than or equal to zero the value of the configuration {@link #PORT_PARAMETER_NAME} parameter will be used, or the {@link #DEFAULT_PORT} if no configuration parameter is found. */ public Dispatcher ( String name, Configuration configuration, int port ) { The_Configuration = configuration; Port = port; Dispatcher_Name (name); } /** Construct a Dispatcher with a configuration and a specified communication port.

    The {@link #Dispatcher_Name() Dispatcher name} will be the {@link #DEFAULT_DISPATCHER_NAME}.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    @param configuration The Configuration to use to {@link #Configure(Configuration) configure} the Dispatcher when it is {@link #Start() started}. @param port The communications port number. If less than or equal to zero the value of the configuration {@link #PORT_PARAMETER_NAME} parameter will be used, or the {@link #DEFAULT_PORT} if no configuration parameter is found. */ public Dispatcher ( Configuration configuration, int port ) {this (null, configuration, port);} /** Construct a named Dispatcher with a configuration.

    The communication port number will be obtained from the configuration {@link #PORT_PARAMETER_NAME} parameter, or the {@link #DEFAULT_PORT} will be used if no configuration parameter is found.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    @param name The {@link #Dispatcher_Name(String) Dispatcher name}. @param configuration The Configuration to use to {@link #Configure(Configuration) configure} the Dispatcher when it is {@link #Start() started}. */ public Dispatcher ( String name, Configuration configuration ) {this (name, configuration, 0);} /** Construct a Dispatcher with a configuration.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    The {@link #Dispatcher_Name() Dispatcher name} will be the {@link #DEFAULT_DISPATCHER_NAME}.

    The communication port number will be obtained from the configuration {@link #PORT_PARAMETER_NAME} parameter, or the {@link #DEFAULT_PORT} will be used if no configuration parameter is found.

    @param configuration The Configuration to use to {@link #Configure(Configuration) configure} the Dispatcher when it is {@link #Start() started}. */ public Dispatcher ( Configuration configuration ) {this (null, configuration, 0);} /** Construct a named Dispatcher.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    When the Dispatcher is {@link #Configure(Configuration) configured} a default configuration source will be sought.

    The communication port number will be obtained from the configuration {@link #PORT_PARAMETER_NAME} parameter, or the {@link #DEFAULT_PORT} will be used if no configuration parameter is found.

    @param name The {@link #Dispatcher_Name(String) Dispatcher name}. */ public Dispatcher ( String name ) {this (name, null, 0);} /** Construct a Dispatcher.

    : Constructing a Dispatcher does not {@link #Start() start} it operating.

    The {@link #Dispatcher_Name() Dispatcher name} will be the {@link #DEFAULT_DISPATCHER_NAME}.

    When the Dispatcher is {@link #Configure(Configuration) configured} a default configuration source will be sought.

    The communication port number will be obtained from the configuration {@link #PORT_PARAMETER_NAME} parameter, or the {@link #DEFAULT_PORT} will be used if no configuration parameter is found. */ public Dispatcher () {this (null, null, 0);} /*============================================================================== Accessors */ /** Get the default Dispatcher name.

    @return The default Dispatcher name. @see #Default_Dispatcher_Name(String) */ public static String Default_Dispatcher_Name () {return Default_Dispatcher_Name;} /** Set the default Dispatcher name.

    @param name The default Dispatcher name. If null, the {@link #DEFAULT_DISPATCHER_NAME} is used. */ public static void Default_Dispatcher_Name ( String name ) { if (name == null) name = DEFAULT_DISPATCHER_NAME; Default_Dispatcher_Name = name; } /** Get the Dispatcher name.

    @return The Dispatcher name. @see #Dispatcher_Name(String) */ public String Dispatcher_Name () {return Dispatcher_Name;} /** Set the Dispatcher name.

    The {@link #NAME_PARAMETER_NAME} in the Dispatcher {@link #Identity() identity} is set to the Dispatcher name.

    @param name The Dispatcher name. If null, the {@link #Default_Dispatcher_Name() default Dispatcher name} is used. @return This Dispatcher. */ public synchronized Dispatcher Dispatcher_Name ( String name ) { if (name == null) name = Default_Dispatcher_Name; Dispatcher_Name = name; // Reset the Identity name. if (Identity == null) { try {Identity (null);} catch (PVL_Exception exception) {} } Identity.Set (NAME_PARAMETER_NAME, Dispatcher_Name); return this; } /** Get the Dispatcher communication port number.

    @return The communication port number for this Dispatcher. */ public int Port () {return Port;} /** Get the Dispatcher Hello broadcast port number.

    @return The Hello broadcast port number for this Dispatcher. */ public int Hello_Port () {return Hello_Port;} /** Get the Dispatcher Hello broadcast multicast address.

    @return The Hello broadcast multicast address String for this Dispatcher. */ public String Hello_Address () {return Hello_Address;} /** Get the Dispatcher location.

    The format of the location string is:

    host[:port]

    The host may be a hostname or IP address. The hostname is {@link Host#Full_Hostname() fully qualified}. The communications {@link #Port() port number} is appended after a colon (':') delimiter.

    @return A String specifying the location of this Dispatcher. */ public String Location () {return Host.Full_Hostname () + ':' + Port;} /** Get the Dispatcher identity.

    @return A Message containing the Dispatcher identification information. This will be null if, and only if, the identity Message could not be assembled. @see #Identity(Message) */ public Message Identity () { try {return new Message (Identity);} catch (PVL_Exception exception) { // The Identity Message has been corrupted! try { Identity (null); return new Message (Identity); } catch (PVL_Exception except) {/* Shouldn't happen */} } return null; } /** Set the Dispatcher identity.

    The following parameters will always be set in the identity Message:

    {@link #ACTION_PARAMETER_NAME}
    Set to {@link #IDENTITY_ACTION}.
    {@link #NAME_PARAMETER_NAME}
    Set to the {@link #Dispatcher_Name() Dispatcher name}.
    {@link #CLASS_ID_PARAMETER_NAME}
    Set to the {@link #Dispatcher_ID}.
    {@link Configuration#HOST}
    Set to the {@link Host#FULL_HOSTNAME}.
    {@link Configuration#USER}
    Set to the account name under which the Dispatcher is running.

    @param identity A Message containing the Dispatcher identification information. If null an empty Message is provided before setting the required parameters. @return This Dispatcher. @throws PVL_Exception if the identity Message can not be assembled either because the specified identity Message can not be copied or the required parameters can not be set. */ protected synchronized Dispatcher Identity ( Message identity ) throws PVL_Exception { if (identity == null) Identity = new Message (); else Identity = new Message (identity); Identity .Set (ACTION_PARAMETER_NAME, IDENTITY_ACTION) .Set (NAME_PARAMETER_NAME, Dispatcher_Name) .Set (CLASS_ID_PARAMETER_NAME, Dispatcher_ID) .Set (Configuration.HOST, Host.FULL_HOSTNAME) .Set (Configuration.USER, System.getProperty ("user.name")); return this; } /** Get the maximum time to wait for a response to a new client identify request.

    @return The identify reponse timeout (seconds). @see #Identify_Timeout(int) */ public int Identify_Timeout () {return Identify_Timeout;} /** Set the maximum time to wait for a response to a new client identify request.

    If a new client connection does not respond to the identity request before the timeout expires the client connection is dropped.

    @param seconds The identify reponse timeout (seconds). If less than or equal to zero the DEFAULT_IDENTIFY_TIMEOUT will be used. @return This Dispatcher. @see #Identify_Timeout(int) */ public Dispatcher Identify_Timeout ( int seconds ) { if (seconds < MINIMUM_IDENTIFY_TIMEOUT) seconds = MINIMUM_IDENTIFY_TIMEOUT; Identify_Timeout = seconds; return this; } /** Enable or disable automatic reporting of the current Messagers list whenever it changes.

    Whenever a client connects or disconnects a report is sent to all Messengers that have {@link #Start_Messenger_Reporting(Messenger) requested connection reports} that provides the identities of all Messengers in the modified list of connected Messagers. For Message protocol operations that may generate a flurry of connection events it can be desirable to suspend automatic connection event reporting until after the protocol operation has been completed. This can significantly reduce the amount of connection list change processing that clients are required to manage.

    @param enabled If true whenever the list of connected Messagers changes it will be reported to all Messengers that have requested connection reports; if false reports will only be provided on demand. @return This Dispatcher. @see #Report_Messengers() */ public Dispatcher Report_Messenger_Connections ( boolean enabled ) { Report_Messenger_Connections = enabled; return this; } /** Test if automatic reporting of the current Messagers list is enabled.

    @return true if automatic reporting of the current Messagers list is enabled; false otherwise. @see #Report_Messenger_Connections(boolean) */ public boolean Report_Messenger_Connections () {return Report_Messenger_Connections;} /** Get any Exception that might have occured when the Dispatcher was used as a {@link #run() Runnable}.

    When used as a Runnable the Dispatcher connection listener must first be {@link #Start() started}. If this throws an exception it is held since it can not be thrown from a Runnable.

    @return The Exception that was thrown when the Dispatcher connection listener was {@link #Start() started} as a result of {@link #run() running} the Dispatcher. This will be null if no exception was thrown. */ public Exception Run_Exception () {return Run_Exception;} /*============================================================================== Configuration */ /** Get the default configuration source.

    The default configuration source is tried if no Configuration is supplied to {@link #Configure(Configuration) configure} the Dispatcher and a configuration source based on the {@link #Dispatcher_Name() Dispatcher name} can not be found.

    @return The name of the default source file. This is the {@link #Default_Dispatcher_Name() default Dispatcher name} plus the ".conf" extension. */ public static String Default_Configuration_Source () {return Default_Dispatcher_Name + ".conf";} /** Configures the Dispatcher.

    If no Configuration is provided a search will be made for a default configuration source. The {@link #Dispatcher_Name() Dispatcher name} plus the ".conf" extension will be tried first. If this fails, the {@link #Default_Configuration_Source() default configuration source} will be tried.

    The Configuration is set to be {@link Configuration#Case_Sensitive(boolean) case insensitive} before parameters are sought.

    Configuration parameters that may be used:

    {@link #PORT_PARAMETER_NAME}
    If the communications port number was not set when the Dispatcher was constructed the value of this parameter will be used. The default value is {@link #DEFAULT_PORT}.
    {@link #IDENTIFY_TIMEOUT_PARAMETER_NAME}
    The number of seconds to wait for a Messenger client connection to respond to the identify message it is sent. The default value is {@link #DEFAULT_IDENTIFY_TIMEOUT}; the minium value is {@link #MINIMUM_IDENTIFY_TIMEOUT}.
    {@link #PASSWORD_PARAMETER_NAME}
    The Messenger client authentication password. N.B.: If no password is provided, or it is emtpy, and {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true unauthenticated connections will be allowed.
    {@link #LOG_PATHNAME_PARAMETER_NAME}
    If {@link #Logging(boolean) logging} has not been enabled, and a log pathname is found, the pathname is used to set the {@link #Log_Pathname(String) log file} and logging is then enabled. N.B.: A configuration log pathname will not override an application set log file if logging has been enabled by the application.
    {@link #HELLO_PORT_PARAMETER_NAME}
    After the Dispatcher starts and is ready to receive connections it will broadcast a {@link #HELLO_MESSAGE} on this port to notify any clients that may listening that they may open a connection. If the port number is zero no Hello broadcast will be sent. {@link #DEFAULT_HELLO_PORT} is the default port number. If the port number is not positive no broadcast will be done.
    {@link #HELLO_ADDRESS_PARAMETER_NAME}
    This address is expected to be a private multicast address suitable for use in broadcasting the Hello message. The time-to-live of datagrams that are sent is zero to keep them on the local system. {@link #DEFAULT_HELLO_ADDRESS} is the default multicast address. If the address is empty no broadcast will be done.

    N.B.: All parameters are sought in the {@link #Config_Pathname(String) in the Configuration Group} with the {@link #Dispatcher_Name() Dispatcher name}.

    @param configuration The Configuration to use. If null and defatult configuration source will sought. @throws Configuration_Exception If there was a problem parsing the configuration source, accessing its contents, or establishing a log file. @throws SecurityException If the connection authentication keys could not be generated, no {@link #PASSWORD_PARAMETER_NAME} was found but {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED} is false, or a log file could not be opened due to a security violation. */ protected void Configure ( Configuration configuration ) throws Configuration_Exception, SecurityException { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Configure"); if (configuration == null) { String source = Dispatcher_Name + ".conf"; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Trying " + source); try {configuration = new Configuration (source);} catch (IllegalArgumentException exception) {} if (configuration == null && ! Dispatcher_Name.equals (Default_Dispatcher_Name)) { // Fallback to the default name. source = Default_Dispatcher_Name + ".conf"; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Trying " + source); try {configuration = new Configuration (source);} catch (IllegalArgumentException exception) {} } if (configuration == null) throw new Configuration_Exception (ID + NL + "Unable to find the default configuration file \"" + Dispatcher_Name + ".conf\"" + ((! Dispatcher_Name.equals (Default_Dispatcher_Name)) ? (" nor \"" + Default_Dispatcher_Name + ".conf\".") : ".")); } The_Configuration = configuration; The_Configuration.Case_Sensitive (false); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Configuration -" + NL + The_Configuration.Description ()); if (Port <= 0) Port = (int)The_Configuration.Get_Number (Config_Pathname (PORT_PARAMETER_NAME), DEFAULT_PORT); Identify_Timeout ((int)The_Configuration.Get_Number (Config_Pathname (IDENTIFY_TIMEOUT_PARAMETER_NAME), DEFAULT_IDENTIFY_TIMEOUT)); // Authentication password. Password = The_Configuration.Get_One (Config_Pathname (PASSWORD_PARAMETER_NAME)); if (Password != null && Password.length () == 0) Password = null; if (Password != null) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Authentication configuration with password"); try { Keys = Authentication.Keys (); Public_Key = Authentication.Public_Key (Keys); } catch (Exception exception) { throw (SecurityException)new SecurityException (ID + NL + "Failed to initialize the authentication keys.") .initCause (exception); } } else if (! UNAUTHENTICATED_CONNECTIONS_ALLOWED) throw new SecurityException (ID + NL + "Unauthenticated connections are not allowed" + NL + "and no " + PASSWORD_PARAMETER_NAME + " parameter was found in the configuration file -" + NL + The_Configuration.Source ()); if (! Logging) { String log_file = The_Configuration.Get_One (Config_Pathname (LOG_PATHNAME_PARAMETER_NAME)); if (log_file != null) { try {Log_Pathname (log_file);} catch (FileNotFoundException exception) { throw new Configuration_Exception (ID + NL + "Configuration " + Config_Pathname (LOG_PATHNAME_PARAMETER_NAME) + " parameter." + NL + exception.getMessage ()); } Logging (true); } } Hello_Port = (int)The_Configuration.Get_Number (Config_Pathname (HELLO_PORT_PARAMETER_NAME), DEFAULT_HELLO_PORT); Hello_Address = The_Configuration.Get_One (Config_Pathname (HELLO_ADDRESS_PARAMETER_NAME)); if (Hello_Address == null) Hello_Address = DEFAULT_HELLO_ADDRESS; if (Hello_Address.length () == 0) Hello_Address = null; if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Configure"); } /** Get an application specifiic configuration parameter pathname.

    If the specified name is null or an {@link Configuration#Is_Absolute_Pathname(String) absolute pathname} it is returned unmodified. Otherwise the name is added to the absolute pathname of the {@link #Dispatcher_Name() Dispatcher name} Group.

    @param name A parameter name. May be null. @return An absolute Configuration parameter pathname (or null if the name is null). */ protected String Config_Pathname ( String name ) { if (name != null && ! Configuration.Is_Absolute_Pathname (name)) return The_Configuration.Path_Delimiter () + Dispatcher_Name + The_Configuration.Path_Delimiter () + name; return name; } /*============================================================================== Logging */ /** Get the default log filename.

    @return The default log filename. This will be the {@link #Default_Dispatcher_Name() default Dispatcher name} plus the ".log" extension. */ public static String Default_Log_Filename () {return Default_Dispatcher_Name + ".log";} /** Get the log file pathname.

    @return The log file pathname. This will be the last {@link #Log_Pathname(String) log pathname} that was set, or the {@link #Default_Log_Filename() default log filename} if a log pathname has not been set. */ public String Log_Pathname () {return Log_Pathname;} /** Set the pathname to the log file.

    If a {@link #Log_Stream() log stream} is already open it is flushed. If it is not the stdout, it is closed.

    If the pathname is {@link #STDOUT} the log stream is set to the {@link System#out stdout} stream. Otherwise the pathname is checked for an existing file. N.B.: An existing file is appended. If the file does not exist an attempt is made to create it. The {@link #Log_Stream() log stream} is put in auto-flush mode.

    @param pathname The pathname to the log file. If null or the empty String the {@link #Default_Log_Filename() default log filename} will be used. @return A String that reports how the log file was established. @throws FileNotFoundException If an existing file could not be appended, possibly because it is not a regular file, or the file could not be created for writing. @see #Log_Stream() */ public synchronized String Log_Pathname ( String pathname ) throws FileNotFoundException { if (pathname == null || pathname.length () == 0) pathname = Default_Log_Filename (); Log_Pathname = pathname; if (Log_Stream != null) { // Shutdown the log stream. Log_Stream.flush (); if (Log_Stream != System.out) Log_Stream.close (); Log_Stream = null; } if (Log_Pathname.equals (STDOUT)) { Log_Stream = System.out; return "Logging to standard output."; } File log_file = new File (Log_Pathname); log_file = log_file.getAbsoluteFile (); String report = null; if (log_file.exists ()) { if (log_file.isFile ()) { // Append to existing file. try {Log_Stream = new PrintStream (new FileOutputStream (log_file, true) , true);} catch (FileNotFoundException exception) { throw new FileNotFoundException (ID + NL +"Unable to append to the log file: " + log_file.getAbsolutePath () + NL + exception.getMessage ()); } } else { throw new FileNotFoundException (ID + NL +"The file " + log_file.getAbsolutePath () + NL +"exists but is not a normal file."); } report = "Appending to log file: " + log_file.getAbsolutePath (); } else { try {Log_Stream = new PrintStream (new FileOutputStream (log_file) , true);} catch (FileNotFoundException exception) { throw new FileNotFoundException (ID + NL +"Unable to create the log file: " + log_file.getAbsolutePath () + NL + exception.getMessage ()); } report = "New log file: " + log_file.getAbsolutePath (); } return report; } /** Get the log stream.

    @return The PrintStream where logging will be written. This will be null if no {@link #Log_Pathname(String) log file} has been established. @see #Log_Pathname(String) */ public PrintStream Log_Stream () {return Log_Stream;} /** Test if logging is enabled.

    @return true if logging is enabled; false otherwise. @see #Logging(boolean) */ public boolean Logging () {return Logging;} /** Enable or disable logging.

    If logging is being enabled from a disabled state, and no {@link #Log_Stream() log stream} is open, the current {@link #Log_Pathname() log pathname} is used to open a log stream.

    If logging is being disabled from an enabled state the log stream is flushed but the log stream remains open.

    N.B.: Logging may be enabled and disabled while the Dispatcher is in operation.

    @param enabled true if logging is to be enabled; false otherwise. @return true if logging was enabled; false otherwise. */ public synchronized boolean Logging ( boolean enabled ) { if (enabled) { if (! Logging) { // Start logging. if (Log_Stream == null) { try { Log_Pathname (Log_Pathname); Logging = true; } catch (FileNotFoundException exception) {} } else Logging = true; } } else if (Logging) { // Stop logging. Logging = false; Log_Stream.flush (); } return Logging; } /** Writes a log report with a timestamp.

    If logging is disabled nothing is done.

    The current date and time is written using the {@link #DATE_FORMAT} on a line by itself. This is followed by the report text. If the report text is null a single empty line is written.

    @param report The report text to be logged. If null a single empty line is written. @see #Logging(boolean) @see #Log_Pathname(String) */ public void Log_Time ( String report ) {Log (new Date (), report);} /** Writes a log report.

    If logging is disabled nothing is done.

    @param report The report text to be logged. If null a single empty line is written. @see #Logging(boolean) @see #Log_Pathname(String) */ public void Log ( String report ) {Log (null, report);} /** Writes an empty log line.

    @see #Log(String) */ public void Log () {Log (null, null);} private void Log ( Date timestamp, String report ) { if (Logging) synchronized (Log_Stream) { if (timestamp != null) Log_Stream.println (NL + DATE_FORMAT.format (timestamp)); if (report != null) Log_Stream.println (report); else Log_Stream.println (); } } /*============================================================================== Listener */ /** Start the Dispatcher.

    If the Dispatcher has already been started nothing is done.

    The Dispatcher is first {@link #Configure(Configuration) configured}.

    If {@link #Logging(boolean) logging} has been enabled a startup message is logged.

    Then the communications socket is established.

    The connection listener is then {@link #run() run} which listens for and handles Messenger client connections.

    N.B.: Once the Dispatcher is started it will continue to run until interrupted, a fatal exception occurs, or the JVM exits.

    @throws Configuration_Exception If there was a problem configuring the Dispatcher. @throws SecurityException If, during configuration, the connection authentication keys could not be generated, no {@link #PASSWORD_PARAMETER_NAME} was found but {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED} is false, or a log file could not be opened due to a security violation. This exception will also be thrown if a security violation was encountered while establishing the communication channel. @throws IOException If there was a problem while establishing the communication channel. */ public void Start () throws Configuration_Exception, SecurityException, IOException { if (Started ()) return; JRE.addShutdownHook (new Thread () {public void run () {Shutdown ();}}); Configure (The_Configuration); Log (NL + START_STOP_MARKER); Log_Time (ID + NL + (ID.equals (Dispatcher_ID) ? "" : (Dispatcher_ID + NL)) + Dispatcher_Name + " start on host " + Host.Full_Hostname () + '/' + Host.IP_Address ()); Bind_to_Socket (); if (Password == null) Log (">>> WARNING <<< Connection authentication is not enabled."); run (); } /** Tests if this Dispatcher has been started.

    @return true If this Dispatcher has started its connection listener; false otherwise. @see #Start() */ public boolean Started () {return Listener != null;} /** Run the connection listener.

    N.B.: A check is first made that the Dispatcher connection listener has been {@link #Started() started} and, if it has not, it is told to {@link #Start() start}. This enables the Dispatcher to be used a Thread Runnable. However, starting the connection listener causes an exception to be thrown the Runnable will not run. In this case the {@link #Run_Exception () run exception} must be specifically retrieved.

    While the listener socket that was bound when the Dispatcher was constructed is open the thread continually listens for new connections. Each connection is a potential new client. Each connection is allocated a new Messenger that uses the socket as its communication channel and this Dispatcher as its {@link Messenger#Employer() employer}.

    Each client must complete a protocol handshake:

    {@link Message#Identify() Identify}
    The handshake is intiated by the Dispatcher {@link #Send(Messenger, Message) sending} the client via the new Messenger an Identify request Message. This Message contains an {@link Message#ACTION_PARAMETER_NAME Action} parameter with the {@link Message#IDENTIFY_ACTION Identify} value. If authentication is required or the Dispatcher has been configured with password a {@link #KEY_PARAMETER_NAME Key} parameter will also be included that has an encoded public key value.
    {@link #Authentication(Message, Message) Authentication}
    The client on receiving the Identify request Message must use the public key it provides to generate authentication information that will be included with its other Identity information. This is done by setting the {@link #KEY_PARAMETER_NAME Key} parameter in a copy of its usual Identity Message to the required password value and then passing both the Identify and Identity Messages to the Authentication utility which will replace the password value of the Identity Key parameter with an encrypted and encoded value created using the public key from the Identify Message. If client authentication is not required no authentication information needs to be provided. The Identity Message is sent by the client in response to the Identify Message.
    {@link Message#Identity() Identity}
    The Dispatcher expects to {@link Messenger#Receive(int) receive} an Identity Message in response to its Identify request within the {@link #Identify_Timeout() timeout} period or the Messenger will be told that it is {@link Messenger#Done(String, Exception) done} which will drop the connenction. The identity Message and its route-from addressing is {@link #Log(String) logged}.
    {@link #Authenticate(Message) Authenticate}
    The client's Identity Message is used for authentication. If {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true and the Dispatcher has not been {@link #Configure(Configuration) configured} with a password then client Identity authentication always succeeds. When password authentication is required for the Identity authentication to succeed the Identity Message must include a {@link #KEY_PARAMETER_NAME} parameter with a value that can be decoded and decrypted using the Dispatcher's private key to produce a password value that is identical to the one with which it has been configured.
    Conclusion
    If authentication of the client's Identity Message succeeded the Dispatcher will conclude the handshake by sending its own {@link #Identity() Identity} Message to the client. If authentication failed a {@link #NACK(Messenger, Message) NACK} Message will be sent containing the client's Identity without any Key parameter plus an {@link #EXPLANATION_PARAMETER_NAME Explanation} parameter saying that the Identity did not authenticate.
    If any part of the handshake fails the Messenger is told that it is {@link Messenger#Done(String) done} which closes down the Messenger and closes its connection.

    After conclusion of the protcol handshake the route-to address list of the identity Message is set to its route-from list and the Message is bound the Messenger as its (@link Messenger#Identity(Message) identity}. The Messenger is added to the list of connected Messengers and is told to asynchronously listen for Messages. Then the list of {@link #Start_Messenger_Reporting(Messenger) Report_to_Messengers} are sent a Message that includes the {@link #Messenger_Identities(Messenger, String) identities of the connected Messengers}.

    Each connection step is logged.

    @see SocketChannel */ public void run () { if (! Started ()) { try {Start ();} catch (Exception exception) { Run_Exception = exception; return; } } SocketChannel client; Messenger messenger; Message message, identity; Exception exception; String report; if (Hello_Port > 0 && Hello_Address != null && Hello == null) { Log ("Sending the \"" + HELLO_MESSAGE + "\" broadcast to multicast address " + Hello_Address + " via port " + Hello_Port + '.' + NL); Hello = new Say_Hello (); Hello.start (); } Log ("Listening for client connections ..." + NL); while (Listener.isOpen ()) { client = null; if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("... Dispatcher: Listening for connection ..."); try {client = Listener.accept ();} catch (IOException except) { if (except instanceof AsynchronousCloseException) // The Listener has been closed. break; Log_Time ("Connection exception -" + NL + except); } catch (SecurityException except) { Log_Time ("Client connection prohibited -" + NL + except); } catch (Exception except) { Log_Time ("Unexpected connection exception:"); if (Logging) except.printStackTrace (Log_Stream ()); System.exit (EXIT_UNKNOWN_EXCEPTION); } if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("==> Dispatcher: Connection" + ((client == null) ? " (no client)" : "")); if (client == null) continue; // Assign the client a messenger. messenger = new Messenger (this, client); Log_Time ("Client connection: " + NL + messenger); // Ask the client to provide identification. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: Sending the client an " + IDENTIFY_ACTION + " Message"); /* The public key to be used for authentication is included. However, if authentication is not enabled there will be no public key and so the key parameter will not be set. */ if (! Send (messenger, Message.Identify () .Set (KEY_PARAMETER_NAME, Public_Key))) { Log ("Unable to send to the client." + NL); messenger = null; continue; } // Get the client identification. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: Receiving the client " + IDENTIFY_ACTION + " Message"); message = null; identity = null; exception = null; report = null; try { message = messenger.Receive (Identify_Timeout); identity = new Message (message) .Set (KEY_PARAMETER_NAME, null); } catch (IOException except) { exception = except; report = "The Stage_Manager did not receive an identity from the client."; } catch (PVL_Exception except) { exception = except; report = "Invalid identity message."; } catch (Exception except) { Log_Time ("Messenger exception:"); if (Logging) except.printStackTrace (Log_Stream ()); System.exit (EXIT_UNKNOWN_EXCEPTION); } if (message == null) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: " + exception); Log (report + NL + exception.getMessage () + NL); messenger.Done (report, exception); messenger = null; continue; } identity.Remove (ACTION_PARAMETER_NAME); Log (identity.Get (NAME_PARAMETER_NAME) + " client identity -" + NL +"Route from - " + identity.Route_From ().Description () + NL + identity); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: Client identity -" + NL +" Route from - " + identity.Route_From ().Description () + NL + identity); // Authenticate the client identification. if (! Authenticate (message)) { report = "The Stage_Manager could not authenticate the client."; if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: " + report); Log (report + NL); NACK (messenger, identity.Set (EXPLANATION_PARAMETER_NAME, report)); messenger.Done (report); continue; } Log (); // Send the server identity in response. Send (messenger, Identity ().Reply_To (identity)); // Move the Route_From list to the Route_To list. identity.Reply_To (identity); // Apply the identification Message to the Messenger. messenger.Identity (identity); // Add the Messenger to the connected list. synchronized (Messengers) { if (Messengers.contains (messenger)) { // This should never happen. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("--> Dispatcher: Removing redundant Messenger connection!"); Log ("Multiple Messenger connection!" + NL +"Previous connection dropped:" + NL + messenger); Messenger_Quit (messenger, "Multiple Messenger connection dropped."); } Messengers.add (messenger); } // Start the Messenger listening to the client. messenger.Listen_for_Messages (); // Notify interested clients of the new Messenger. if (Report_Messenger_Connections) Report_Messengers (); } Log_Time ("Server socket closed."); Listener = null; } /** Disconnect a Messenger.

    The messenger is removed from the list of connected Messengers and the list of Messengers to receive reports of connected Messengers. If the Messenger was removed from the connected list, the disconnection is logged with the Messenger details and the report (if non-null), and the Messenger is told that it is {@link Messenger#Done(String) done} in case it has not already initiated its communication shutdown procedures.

    When the Messengers list is empty the shutdown-in-progress flag, that may have been set in the {@link #Shutdown() shutdown} procedure, is reset.

    @param messenger The Messenger to be disconnected. @param report A report to be logged. May be null. */ protected void Messenger_Quit ( Messenger messenger, String report ) { if ((DEBUG & DEBUG_MESSENGERS) != 0) System.out.println (">>> Dispatcher.Messenger_Quit:" + NL + messenger + NL + report); boolean removed = false; synchronized (Messengers) { removed = Messengers.remove (messenger); Report_to_Messengers.remove (messenger); } if ((DEBUG & DEBUG_MESSENGERS) != 0) System.out.println (" The Messenger was " + (removed ? "" : "not ") + "removed"); if (removed) { Log_Time (messenger.Identity ().Get (NAME_PARAMETER_NAME) + " Messenger disconnected:" + NL + messenger + NL +"Route from - " + messenger.Identity ().Route_To ().Description () + NL + messenger.Identity ()); if (report != null) Log (report); Log (); // Make sure the Messenger is done. if (report == null) report = ID + NL; else report = ID + NL + report; messenger.Done (report); if (Report_Messenger_Connections) { // Notify interested clients of the Messenger disconnection. if ((DEBUG & DEBUG_MESSENGERS) != 0) System.out.println (" Reporting new Messengers list"); Report_Messengers (); } } if (Messengers.size () == 0) Shutdown_In_Progress = false; if ((DEBUG & DEBUG_MESSENGERS) != 0) System.out.println ("<<< Dispatcher.Messenger_Quit"); } /** Disconnect a Messenger.

    If a message is specified and it contains an {@link #EXPLANATION_PARAMETER_NAME} and/or an {@link #EXCEPTION_PARAMETER_NAME} the values of those parameters are assembled into report to be included in the log. If there are any additional parameters, other than the {@link #ACTION_PARAMETER_NAME}, a description of all remaining parameters are also included in the log report.

    @param messenger The Messenger to be disconnected. @param message A Message to be logged. May be null. @see #Messenger_Quit(Messenger, String) */ protected void Messenger_Quit ( Messenger messenger, Message message ) { String report = null; if (message != null) { String string; string = Message.Unescape (message.Get (EXPLANATION_PARAMETER_NAME)); if (string != null) { report = string; message.Remove (EXPLANATION_PARAMETER_NAME); } string = message.Get (EXCEPTION_PARAMETER_NAME); if (string != null && ! string.startsWith ("java.io.EOFException")) { if (report == null) report = ""; else report += NL; report += Message.Unescape (string); } message.Remove (EXCEPTION_PARAMETER_NAME); message.Remove (ACTION_PARAMETER_NAME); if (message.List_Size () != 0) { if (report == null) report = ""; else report += NL; report += message; } } Messenger_Quit (messenger, report); } private volatile boolean Shutdown_In_Progress = false; /** Shutdown the Dispatcher.

    The communications channel is closed. If there are any registered Messengers they are told they are {@link Messenger#Done(String) Done} and a shutdown-in-progress flag is set. This flag is checked up to five times with a two second delay between checks until either the flag is found to be reset or the maximum number of checks have been done. Finally a shutdown notice is logged that indicates whether the shutdown of all the Messegers was completed. */ protected void Shutdown () { try {Listener.close ();} catch (IOException exception) {} try {Thread.sleep (1000);} catch (InterruptedException exception) {} int index = Messengers.size (); if (index > 0) { Shutdown_In_Progress = true; Log_Time ("Shutdown of the " + Dispatcher_Name () + " initiated."); while (--index >= 0) Messengers.get (index).Done ("The " + Dispatcher_Name () + " is shutting down."); } index = 5; while (index-- > 0) { if (Shutdown_In_Progress) { try {Thread.sleep (2000);} catch (InterruptedException exception) {} } } Log_Time ("Shutdown of the " + Dispatcher_Name () + " Messengers " + ((Messengers.size () == 0) ? "is" : "did not fully") + " complete." + NL + NL + START_STOP_MARKER); } /** Get a Messenger for an address.

    The list of connected Messengers is searched for one that has an {@link Messenger#Address() address} that matches the one that is specified.

    @param address The address String associated with a Messenger. If null or the empty String null is returned. @return The connected Messenger having the specified address, or null if no Messenger with that address was found. */ protected Messenger Lookup_Messenger ( String address ) { if (address != null && address.length () != 0) { synchronized (Messengers) { Messenger messenger; int index = 0, total = Messengers.size (); while (index < total) { messenger = Messengers.get (index++); if (address.equals (messenger.Address ())) return messenger; } } } return null; } /*------------------------------------------------------------------------------ Communications Channel */ private void Bind_to_Socket () throws IOException, SecurityException { if (Listener != null && Listener.isOpen ()) { try {Listener.close ();} catch (IOException exception) { Log_Time ("Problem closing the server socket:" + NL + exception.getMessage () + NL); } } Log ("Initializing the server socket on port " + Port + '.'); try { if ((DEBUG & DEBUG_BIND) != 0) System.out.println (" Opening ServerSocketChannel"); Listener = ServerSocketChannel.open (); if ((DEBUG & DEBUG_BIND) != 0) System.out.println (" Configure for blocking"); Listener.configureBlocking (true); if ((DEBUG & DEBUG_BIND) != 0) System.out.println (" Setting socket reuse address"); Listener.socket ().setReuseAddress (true); if ((DEBUG & DEBUG_BIND) != 0) System.out.println (" Binding"); Listener.socket ().bind (new InetSocketAddress (Port)); } catch (IOException exception) { throw new IOException (ID + NL + "Unable to construct a ServerSocketChannel on port " + Port + '.' + NL + exception.getMessage ()); } catch (SecurityException exception) { throw new SecurityException (ID + NL + "Binding a ServerSocket to port " + Port + " is prohibited." + NL + exception.getMessage ()); } } /*============================================================================== Authentication */ /** Provide client identity authentication information.

    During the handshake protocol, while establishing a conneciton to a Dispatcher, authentication information will need to be exchanged unless {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED}. In the {@link #IDENTIFY_ACTION} Message that the Dispatcher sends to initiate the handshake for a new client connection a {@link #KEY_PARAMETER_NAME} parameter will provide a public key value that must be used to produce an encoded password value returned in the {@link #KEY_PARAMETER_NAME} parameter of the required {@link #IDENTITY_ACTION} Message response.

    The authentication key in the Identify Message is used with the authentication key in the Identity Message to produce an encoded authentication key in the Identity Message. If either the Identify or Identity Message do not have an authentication key then the returned Message will not have this parameter. The mechanism by which the Identify key is used to encode the Identity key is opaque.

    N.B.: The Identity Message that is provided is modified, not copied.

    @param identify A Message from which to obtain a {@link #KEY_PARAMETER_NAME} parameter with a public key authentication value. If null the identity Message is returned with any {@link #KEY_PARAMETER_NAME} parameter removed. @param identity A Message from which to obtain a {@link #KEY_PARAMETER_NAME} parameter with a private password authentication value. The value is replaced with encoded authentication information that combines the public key with the password. If null null is returned. @return The possibly modified identity Message. This will have a modified {@link #KEY_PARAMETER_NAME} parameter if, and only if, the original value was successfully encoded into the appropriate authentication response value. @see Authentication */ public static Message Authentication ( Message identify, Message identity ) { if (identify == null) identity.Set (KEY_PARAMETER_NAME, null); else if (identity != null) { if ((DEBUG & DEBUG_AUTHENTICATE) != 0) System.out.println (">-< Authentication: " + identity.Get (NAME_PARAMETER_NAME)); String key = identify.Get (KEY_PARAMETER_NAME), password = identity.Get (KEY_PARAMETER_NAME); identity.Set (KEY_PARAMETER_NAME, Authentication.Encoded_Password (password, key)); } return identity; } /** Authenticate a client identity.

    If no password was obtained from the {@link #Configure(Configuration) configuration} and {@link #UNAUTHENTICATED_CONNECTIONS_ALLOWED} is true, the identity is accepted without further authentication.

    Authentication requires that the identity contain a {@link #KEY_PARAMETER_NAME} parameter value that is used as the {@link #Authentication(Message, Message) encoded authentication password} to {@link Authentication#Authenticate(KeyPair, String, String) authenticate} the client identity. The decoded authentication password from the identity, after being decrypted using the Dispatcher's private key, must match the Dispatcher password.

    @return true if the identity is authenticated or unauthenticated connections are allowed. false if the Dispatcher does not have a password and unauthenticated connections are not allowed, the identity does not contain a Key parameter, or its decoded and decrypted value does not match the password. @see Authentication */ protected boolean Authenticate ( Message identity ) { if ((DEBUG & DEBUG_AUTHENTICATE) != 0) System.out.println (">>> Authenticate: " + NL + identity); if (Password == null) { // Unauthenticated connection. if ((DEBUG & DEBUG_AUTHENTICATE) != 0) System.out.println ("<<< Authenticate: unauthenticated connection"); return UNAUTHENTICATED_CONNECTIONS_ALLOWED; } boolean authenticated = Authentication.Authenticate (Keys, identity.Get (KEY_PARAMETER_NAME), Password); if ((DEBUG & DEBUG_AUTHENTICATE) != 0) System.out.println ("<<< Authenticate: " + authenticated); return authenticated; } /*============================================================================== Messages */ // Message_Delivered_Listener /** Get the identity of this Dispatcher.

    This method implements the {@link Message_Delivered_Listener} interface.

    @return A Message containing the Dispatcher {@link #Identity(Message) identity} information. @see #Identity() */ public Message Listener_Identity () {return Identity ();} // Message_Delivered_Listener /** Receives delivery of a message.

    This method implements the {@link Message_Delivered_Listener} interface.

    The following {@link Message#Action() message actions} are recognized:

    {@link #IDENTIFY_ACTION}
    Replies with the {@link #Listener_Identity() identity Message}.
    {@link #MESSENGERS_REPORT_ACTION}
    Replies with a list of {@link #Messenger_Identities(Messenger, String) currently connected messenger identities} that have been selected by the request message.
    {@link #START_MESSENGER_REPORTING_ACTION}
    {@link #Start_Messenger_Reporting(Messenger) Starts connected Messenger reporting} for the requesting Messenger.
    {@link #STOP_MESSENGER_REPORTING_ACTION}
    {@link #Stop_Messenger_Reporting(Messenger) Stops connected Messenger reporting} for the requesting Messenger.
    {@link #LINK_MESSENGER_ACTION}
    Direct {@link #Messenger_Link(Messenger, Message)} Messenger-to-Messenger communication} is established between the requesting Messenger and another Messenger for which an address is provided.
    {@link #UNLINK_MESSENGER_ACTION}
    Direct {@link #Messenger_Unlink(Messenger, Message)} Messenger-to-Messenger communication} is removed between the requesting Messenger and another Messenger for which an address is provided.
    {@link #DONE_ACTION}
    The Messenger associated with this Message has {@link #Messenger_Quit(Messenger, Message) quit} and is to be disconnected.
    {@link #UNDELIVERABLE_ACTION}
    A Message that was {@link #Send(Messenger, Message) sent} could not be delivered. The undeliverable Message is logged.
    {@link #NACK_ACTION}
    A Message that was {@link #Send(Messenger, Message) sent} was rejected. The rejected Message is logged.

    Any other messages are logged as unrecognized and a {@link #NACK(Messenger, Message) NACK} is sent back to the Messenger from which the message was received.

    @param event The {@link Message_Delivered_Event} containing the Message that was delivered and a reference to the Messenger that sent it. */ public void Message_Delivered ( Message_Delivered_Event event ) { Messenger messenger = (Messenger)event.getSource (); String action = event.Message.Action (); if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Dispatcher.Message_Delivered:" + NL +" From " + messenger.Identity ().Get (NAME_PARAMETER_NAME) + " -" + NL + messenger + NL + event.Message.Routing () + NL + event.Message); try { if (IDENTIFY_ACTION.equals (action)) Send (messenger, Listener_Identity () .Reply_To (event.Message)); else if (MESSENGERS_REPORT_ACTION.equals (action)) Send (messenger, Messenger_Identities (messenger, event.Message.Get (NAME_PARAMETER_NAME)) .Reply_To (event.Message)); else if (START_MESSENGER_REPORTING_ACTION.equals (action)) Start_Messenger_Reporting (messenger); else if (STOP_MESSENGER_REPORTING_ACTION.equals (action)) Stop_Messenger_Reporting (messenger); else if (LINK_MESSENGER_ACTION.equals (action)) Messenger_Link (messenger, event.Message); else if (UNLINK_MESSENGER_ACTION.equals (action)) Messenger_Unlink (messenger, event.Message); else if (STATUS_REPORT_ACTION.equals (action)) Status_Report (messenger, event.Message); else if (DONE_ACTION.equals (action)) Messenger_Quit (messenger, event.Message); else if (UNDELIVERABLE_ACTION.equals (action)) Log_Time ("Undeliverable message from " + messenger.Identity ().Get (NAME_PARAMETER_NAME) + " -" + NL + messenger + NL + event.Message); else if (NACK_ACTION.equals (action)) Log_Time ("Rejected message from " + messenger.Identity ().Get (NAME_PARAMETER_NAME) + " -" + NL + messenger + NL + event.Message); else { Log_Time ("Unrecognized message from " + messenger.Identity ().Get (NAME_PARAMETER_NAME) + " -" + NL + messenger + NL + event.Message); NACK (messenger, event.Message); } } catch (Exception exception) { if (Logging) { Log_Time ("An exception occured while delivering a message -" + NL + messenger + NL + event.Message.Routing () + NL + event.Message); exception.printStackTrace (Log_Stream ()); } } if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Dispatcher.Message_Delivered"); } /*============================================================================== Message handlers */ /** Send a Message via a Messenger.

    If failure reporting is enabled and the Messenger throws an exception while sending the Message this is logged with the Messenger description, the Message routing addresses, the Message description and the exception description. In any case if the exception is an IOException the Messenger is {@link #Messenger_Quit(Messenger, String) quit}.

    @param messenger The Messenger used to send the Message. If null nothing is done. @param message The Message to be sent. If null nothing is done. @param report_failure true if failure reporting is enabled; false otherwise. @return true if the Message was successfully sent; false otherwise. */ protected boolean Send ( Messenger messenger, Message message, boolean report_failure ) { if (messenger == null || message == null) return false; if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println (">>> Dispatcher.Send: To " + messenger.Address () + NL + messenger + NL + message.Routing () + NL + message); boolean sent = true; if (messenger.Is_Connected ()) { try {messenger.Send (message);} catch (Exception exception) { if (report_failure) Log_Time ("Unable to send a message." + NL + messenger + NL + message.Routing () + NL + message + NL + exception + NL); if (exception instanceof IOException) Messenger_Quit (messenger, exception.toString ()); sent = false; } } else sent = false; if ((DEBUG & DEBUG_MESSAGES) != 0) System.out.println ("<<< Dispatcher.Send: " + sent); return sent; } /** Send a Message via a Messenger.

    The Message is {@link #Send(Messenger, Message, boolean) sent} with failure reporting enabled.

    @param messenger The Messenger used to send the Message. If null nothing is done. @param message The Message to be sent. If null nothing is done. @return true if the Message was successfully sent; false otherwise. @see #Send(Messenger, Message, boolean) */ protected boolean Send ( Messenger messenger, Message message ) {return Send (messenger, message, true);} /** Assemble a Message that includes the identities of selected connected Messengers.

    A connected Messenger is the client Messenger that initiated a connection, completed the identification Message handshake with the Dispatcher Messenger allocated to the client, and that is currently active (in the Messengers). Connected Messengers may be selected by {@link #NAME_PARAMETER_NAME} value. If no name is specified all connected Messengers are selected. However, the source Messenger to whom the Message will be sent is never selected.

    The assembled Message includes an {@link #IDENTITY_ACTION} group for each selected Messenger. The parameters in this group are the {@link Messenger#Identity() identity} Message parameters bound to the Messenger. In addition, a {@link #ROUTE_TO_PARAMETER_NAME} is added to each identity that is an array of Messenger {@link Messenger#Address() addresses} for each Messenger on the route from the source Messenger to the connected Messenger. The Value of this parameter can be used to set the {@link Message#Route_To(Value) route-to list} of a Message to be sent from the source Messenger to the destination connected Messenger.

    Since the route-to list of each connected Messenger {@link Messenger#Identity() identity Message} is in Dispatcher-to-client order the route-to list of the source Messenger must be reversed to produce a correct route-to address list in source-to-client order. This reversed address list is then completed with the route-to list of the client Messenger being identified. N.B.: Message route-to address lists are in LIFO order: the last address of the list is for the first Messenger on the transmission route, and the first address is for the destination client Messenger.

    @param source_messenger The Messenger to whom the returned Message is intended. Route-to address lists for each identified Messenger will be relative to this Messenger. If null, or the Messenger has no {@link Messenger#Identity() identity}, or the identity has no {@link Message#Route_To () route-to list} the returned Message will only contain an {@link #EXPLANATION_PARAMETER_NAME} parameter with a brief message value describing the problem. @param name The {@link #NAME_PARAMETER_NAME} value used to select connected Messengers. If null, all Messengers will be selected. The source_messenger is never selected. @return A Message containing the identity and route-to address list of each selected Messenger. However, if source_messenger is null, has no identity, or the identity has no route-to list the Message will only contain an {@link #EXPLANATION_PARAMETER_NAME} parameter with a brief message value describing the problem. */ public Message Messenger_Identities ( Messenger source_messenger, String name ) { if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println (">>> Dispatcher.Messenger_Identities:" + NL +" For source Messenger -" + NL + source_messenger + NL +" Selecting name - " + name); Message message = Message.Action (MESSENGERS_REPORT_ACTION); if (source_messenger == null) { if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println ("<<< Dispatcher.Messenger_Identities: No source messenger!"); message.Set (EXPLANATION_PARAMETER_NAME, "No source messenger!"); return message; } Message identity = source_messenger.Identity (); if (identity == null) { // This should never happen. if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println ("<<< Dispatcher.Messenger_Identities: No source identity!"); message.Set (EXPLANATION_PARAMETER_NAME, "The source messenger does not have an identity!"); return message; } /* Destination Messenger routing. The identity Message route-to list is in Dispatcher to source Messenger order. This needs to be reversed so the route-to list sent to the source client will be in source Messenger to Dispatcher order; i.e. the route-to list sent to the client must be in the order appropriate for the client's use. */ Value source_addresses = identity.Route_To (); if (source_addresses.Array_Size () == 0) { // This should never happen. if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println ("<<< Dispatcher.Messenger_Identities: No source route-to list!"); message.Set (EXPLANATION_PARAMETER_NAME, "The source messenger identity does not have a route-to list!"); return message; } try {Collections.reverse (source_addresses.Vector_Data ());} catch (PVL_Exception exception) {/* Route_To returns a valid Array */} synchronized (Messengers) { Messenger messenger; int index = -1, total = Messengers.size (); while (++index < total) { messenger = Messengers.get (index); // Don't include the source Messenger. if (messenger != source_messenger) { identity = messenger.Identity (); if (name == null || name.equals (identity.Get (NAME_PARAMETER_NAME))) { try { // Copy the identity parameters. identity = new Message (identity); // Add the source route-to list to the client list. identity.Set (ROUTE_TO_PARAMETER_NAME, identity.Route_To ().Add (source_addresses.Vector_Data ()), 0); // Add the identity to the identities message. message.Add (IDENTITY_ACTION, identity); } catch (PVL_Exception exception) {} } } } } if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println (" Identities -" + NL + message + NL +"<<< Dispatcher.Messenger_Identities"); return message; } /** Sends a report of all the {@link #Messenger_Identities(Messenger, String) Messenger identities} to each of the Messengers that have {@link #Start_Messenger_Reporting(Messenger) requested connection reports}. */ protected void Report_Messengers () { synchronized (Messengers) { if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println (">>> Dispatcher.Report_Messengers: to " + Report_to_Messengers.size () + " reportees"); Messenger messenger; int index = -1, total = Report_to_Messengers.size (); while (++index < total) { try {messenger = Report_to_Messengers.get (index);} catch (ArrayIndexOutOfBoundsException exception) { /* Somehow (?!?) the size of Report_to_Messengers has changed. But the index is no longer in range so they have all been done. */ break; } try {Send (messenger, Messenger_Identities (messenger, null) .Route_To (Route_To (messenger)), false);} // No failure report. catch (PVL_Exception exception) {/* Shouldn't happen */} } } if ((DEBUG & (DEBUG_IDENTITIES | DEBUG_MESSENGERS)) != 0) System.out.println ("<<< Dispatcher.Report_Messengers"); } /** Start automatic Messenger connection reporting for a Messenger.

    The specified Messenger is added, if it is not already present, to the list of Messengers that will be sent the list of all connected {@link #Messenger_Identities(Messenger, String) Messenger identities} whenever a new Messenger connects or disconnects.

    The Messenger is sent the current connected Messengers list.

    @param messenger The Messenger to receive automatic Messenger connection reporting. If null, nothing is done. @see #Stop_Messenger_Reporting(Messenger) */ protected void Start_Messenger_Reporting ( Messenger messenger ) { if (messenger == null) return; synchronized (Messengers) { if (! Report_to_Messengers.contains (messenger)) Report_to_Messengers.add (messenger); } // Send the current list of connected Messengers. try {Send (messenger, Messenger_Identities (messenger, null) .Route_To (Route_To (messenger)), false);} // No failure report. catch (PVL_Exception exception) {/* Shouldn't happen */} } /** Stop automatic Messenger connection reporting for a Messenger.

    The specified Messenger is removed, if it is present, from the list of Messengers that will be sent the list of all connected {@link #Messenger_Identities(Messenger, String) Messenger identities}.

    @param messenger The Messenger to receive automatic Messenger connection reporting. If null, nothing is done. @see #Start_Messenger_Reporting(Messenger) */ protected void Stop_Messenger_Reporting ( Messenger messenger ) { if (messenger == null) return; synchronized (Messengers) {Report_to_Messengers.remove (messenger);} } /** Link two Messengers for I/O forwarding.

    The specified Message must contain an {@link #ADDRESS_PARAMETER_NAME} value that specifies the address of {@link #Lookup_Messenger(String) known Messenger} to which the source Messenger is to be linked. The linked Messenger is {@link Messenger#Add_Forwarding(Messenger) added to the forwarding list} of the source Messenger and the source Messenger is added to the forwarding list of the linked Messenger.

    The specified Message is returned to the source Messenger with the addition of an {@link #ACK_ACTION} token and an {@link #IDENTITY_ACTION} group containing the {@link Messenger#Identity() identity} of the linked Messenger.

    If a Messenger with the address to link to is not known the reply Message contains a {@link #NACK_ACTION} token and an {@link #EXPLANATION_PARAMETER_NAME}, instead of a Messenger identity, that describes the reason that the link could not be done.

    N.B.: If the Message contains either an {@link #ACK_ACTION} or {@link #NACK_ACTION} token nothing is done.

    @param source_messenger The source Messenger that is initiating the Messenger link. If null nothing is done. @param message The Message contain the address of the Messenger to be linked. If null nothing is done. @see #Messenger_Unlink(Messenger, Message) */ protected void Messenger_Link ( Messenger source_messenger, Message message ) { if (source_messenger == null || message == null) return; if (message.Find (ACK_ACTION) != null || message.Find (NACK_ACTION) != null) return; String linked_address = message.Get (ADDRESS_PARAMETER_NAME); Messenger linked_messenger = Lookup_Messenger (linked_address); if (linked_messenger == null) { String explanation = "Can't link to a Messenger with an " + ADDRESS_PARAMETER_NAME + " of " + linked_address; Log_Time (explanation + NL +"in request from: " + source_messenger.Address () + NL + source_messenger + NL + message); // Add the failure explanation to the message. message .Set_Token (NACK_ACTION, 0) .Set (EXPLANATION_PARAMETER_NAME, explanation); } else { Log_Time ("Linking:" + NL + source_messenger + NL + source_messenger.Identity () + NL +"and" + NL + linked_messenger + NL + linked_messenger.Identity ()); // Add messenger forwarding links. source_messenger.Add_Forwarding (linked_messenger); linked_messenger.Add_Forwarding (source_messenger); message.Set_Token (ACK_ACTION, 0); Message identity = linked_messenger.Identity (); if (identity != null) { // Add the linked Messenger identity and routing to the message. try {message.Add (IDENTITY_ACTION, new Message (identity) .Set (ROUTE_TO_PARAMETER_NAME, Route (source_messenger, linked_messenger), 0));} catch (PVL_Exception exception) {/* Shouldn't happen */} } else message.Add (Message.Identity () .Set (ADDRESS_PARAMETER_NAME, linked_address)); } // Send the message back as confirmation. Send (source_messenger, message.Reply_To (message)); } /** Unlink two Messengers.

    The specified Message must contain an {@link #ADDRESS_PARAMETER_NAME} value that specifies the address of {@link #Lookup_Messenger(String) known Messenger} from which the source Messenger is to be unlinked. The linked Messenger is {@link Messenger#Remove_Forwarding(Messenger) removed from the forwarding list} of the source Messenger and the source Messenger is removed from the forwarding list of the linked Messenger.

    N.B.: No reply is sent to the source Messenger.

    @param source_messenger The source Messenger that is initiating the Messenger link. If null nothing is done. @param message The Message contain the address of the Messenger to be linked. If null nothing is done. @see #Messenger_Link(Messenger, Message) */ protected void Messenger_Unlink ( Messenger source_messenger, Message message ) { if (source_messenger == null || message == null) return; String linked_address = message.Get (ADDRESS_PARAMETER_NAME); Messenger linked_messenger = Lookup_Messenger (linked_address); String report = "Unlinking:" + NL + source_messenger + NL + source_messenger.Identity () + NL +"and" + NL; if (linked_messenger != null) report += linked_messenger + NL + linked_messenger.Identity (); else report += "Messenger at address " + linked_address + " (not present)"; Log_Time (report); // Remove messenger forwarding links. source_messenger.Remove_Forwarding (linked_messenger); if (linked_messenger != null) linked_messenger.Remove_Forwarding (source_messenger); } protected static Value Route ( Messenger source_messenger, Messenger client_messenger ) { if (source_messenger == null || client_messenger == null) return null; Message identity = source_messenger.Identity (); if (identity == null) // No source identity. return null; Value source_addresses = identity.Route_To (); if (source_addresses.Array_Size () == 0) // No source route-to list. return null; // Dispatcher-to-source reversed to source-to-Dispatcher. try {Collections.reverse (source_addresses.Vector_Data ());} catch (PVL_Exception exception) { // Shouldn't happen. return null; } identity = client_messenger.Identity (); if (identity == null) // No client identity. return null; Value client_addresses = identity.Route_To (); if (client_addresses.Array_Size () == 0) // No client route-to list. return null; try {client_addresses.Add (source_addresses.Vector_Data ());} catch (PVL_Exception exception) { // Shouldn't happen. return null; } return client_addresses; } /** Get the Message route-to values for a Messenger.

    The route_to values specify the {@link Message#Route_To(Value) Message routing addresses} used by a Messenger when a Message is {@link #Send(Messenger, Message) sent}.

    @param messenger A Messenger for which to get the Message route-to address values @return An Array Value containing the route-to address String Values. */ public static Value Route_To ( Messenger messenger ) { Message identity = messenger.Identity (); if (identity != null) return identity.Route_To (); return null; } /** Send a status report message.

    The status report contains the Dispatcher {@link #Identity() identity} plus its communications {@link #IDENTIFY_TIMEOUT_PARAMETER_NAME}, {@link #PORT_PARAMETER_NAME}, #HELLO_PORT_PARAMETER_NAME} and {@link #HELLO_ADDRESS_PARAMETER_NAME} parameters.

    The Dispatcher identification is followed by a {@link #MESSENGER_STATUS_PARAMETER_NAME} group containing each connected Messenger's {@link Messenger#Identity() identity} with the identities of each of its {@link Messenger#Forwarding_Messengers() forwarding Messengers} and its Message statistics:

    • {@link #MESSAGES_SENT_PARAMETER_NAME} from {@link Messenger#Messages_Sent()}
    • {@link #MESSAGE_BYTES_SENT_PARAMETER_NAME} from {@link Messenger#Message_Bytes_Sent()}
    • {@link #MESSAGES_SENT_DROPPED_PARAMETER_NAME} from {@link Messenger#Messages_Sent_Dropped()}
    • {@link #MESSAGES_SENT_CORRUPTED_PARAMETER_NAME} from {@link Messenger#Messages_Sent_Corrupted()}
    • {@link #MESSAGES_RECEIVED_PARAMETER_NAME} from {@link Messenger#Messages_Received()}
    • {@link #MESSAGE_BYTES_RECEIVED_PARAMETER_NAME} from {@link Messenger#Message_Bytes_Received()}
    • {@link #MESSAGES_RECEIVED_DROPPED_PARAMETER_NAME} from {@link Messenger#Messages_Received_Dropped()}
    • {@link #MESSAGES_RECEIVED_CORRUPTED_PARAMETER_NAME} from {@link Messenger#Messages_Received_Corrupted()}
    • {@link #MESSAGES_UNFORWARDABLE_PARAMETER_NAME} from {@link Messenger#Messages_Unforwardable()}

    Each Messenger that registered an {@link Messenger#Error_Condition() error condition} will also have an {@link #EXCEPTION_PARAMETER_NAME} describing the exception following the Message statistics.

    Following the Messenger identities a {@link #MEMORY_STATUS_PARAMETER_NAME} group contains memory use parameters:

    • {@link #MEMORY_AVAILABLE_PARAMETER_NAME} - the maximum amount of memory available to the Java Runtime Environment.
    • {@link #MEMORY_ALLOCATED_PARAMETER_NAME} - the amount of memory currently allocated by the application.
    • {@link #MEMORY_FREE_PARAMETER_NAME} - the amount of allocated memory currently unused.

    @param source_messenger The Messagner to use to send the status report Message. @param message The Message to {@link Message#Reply_To(Message) reply to}. */ protected void Status_Report ( Messenger source_messenger, Message message ) { Message status_report = Identity () .Set (ACTION_PARAMETER_NAME, STATUS_REPORT_ACTION) .Set (CONFIGURATION_SOURCE_PARAMETER_NAME, The_Configuration.Source ()) .Set (IDENTIFY_TIMEOUT_PARAMETER_NAME, Identify_Timeout) .Set (PORT_PARAMETER_NAME, Port) .Set (HELLO_PORT_PARAMETER_NAME, Hello_Port) .Set (HELLO_ADDRESS_PARAMETER_NAME, Hello_Address) .Reply_To (message), messenger_status = new Message (); messenger_status.Name (MESSENGER_STATUS_PARAMETER_NAME); synchronized (Messengers) { Messenger messenger; Message identity = null; Exception error_condition; int index = -1, total = Messengers.size (); while (++index < total) { messenger = Messengers.get (index); try {identity = new Message (messenger.Identity ());} catch (PVL_Exception exception) {} identity.Name (identity.Get (NAME_PARAMETER_NAME)); identity .Add (messenger.Forwarding_Messengers ()) .Set (MESSAGES_SENT_PARAMETER_NAME, messenger.Messages_Sent ()) .Set (MESSAGE_BYTES_SENT_PARAMETER_NAME, messenger.Message_Bytes_Sent ()) .Set (MESSAGES_SENT_DROPPED_PARAMETER_NAME, messenger.Messages_Sent_Dropped ()) .Set (MESSAGES_SENT_CORRUPTED_PARAMETER_NAME, messenger.Messages_Sent_Corrupted ()) .Set (MESSAGES_RECEIVED_PARAMETER_NAME, messenger.Messages_Received ()) .Set (MESSAGE_BYTES_RECEIVED_PARAMETER_NAME, messenger.Message_Bytes_Received ()) .Set (MESSAGES_RECEIVED_DROPPED_PARAMETER_NAME, messenger.Messages_Received_Dropped ()) .Set (MESSAGES_RECEIVED_CORRUPTED_PARAMETER_NAME, messenger.Messages_Received_Corrupted ()) .Set (MESSAGES_UNFORWARDABLE_PARAMETER_NAME, messenger.Messages_Unforwardable ()); error_condition = messenger.Error_Condition (); if (error_condition != null) identity.Set (EXCEPTION_PARAMETER_NAME, error_condition.toString ()); messenger_status.Add (identity); } } status_report.Add (messenger_status); status_report.Add (new Message () .Set (MEMORY_AVAILABLE_PARAMETER_NAME, JRE.maxMemory ()) .Set (MEMORY_ALLOCATED_PARAMETER_NAME, JRE.totalMemory ()) .Set (MEMORY_FREE_PARAMETER_NAME, JRE.freeMemory ()) .Name (MEMORY_STATUS_PARAMETER_NAME)); Send (source_messenger, status_report); } protected void NACK ( Messenger messenger, Message message ) { Send (messenger, Message.NACK () .Add (ORIGINAL_MESSAGE_PARAMETER_NAME, message) .Reply_To (message), false); // No failure report. } /*============================================================================== Say_Hello Broadcast */ private class Say_Hello extends Thread { public void run () { MulticastSocket socket; DatagramPacket packet; try { socket = new MulticastSocket (); socket.setTimeToLive (0); // Assemble the Datagram packet. byte[] buffer = HELLO_MESSAGE.getBytes (); packet = new DatagramPacket (buffer, buffer.length, InetAddress.getByName (Hello_Address), Hello_Port); } catch (IOException exception) { Log ("Unable to construct the multicast socket." + NL + exception); return; } try { int count = 0; while (count++ < HELLO_COUNT) { socket.send (packet); sleep (HELLO_INTERVAL * 1000); } } catch (IOException exception) { Log ("There was a problem sending the Hello message." + NL + exception); } catch (InterruptedException exception) {} socket.close (); Hello = null; } } /*============================================================================== Application main */ public static void main ( String[] args ) { System.out.println (ID + NL); String configuration_filename = null, log_filename = null, name = ""; int port = 0; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].charAt (1)) { case 'C': // Configuration case 'c': if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') { System.out.println ("Missing configuration filename"); Usage (); } if (configuration_filename != null) { System.out.println ("Multiple configuration files -" + NL + configuration_filename + NL + "and" + NL + args[count]); Usage (); } configuration_filename = args[count]; break; case 'L': // Logging case 'l': if (++count == args.length || (args[count].length () > 1 && args[count].charAt (0) == '-')) { name = Default_Log_Filename (); --count; } else name = args[count]; if (log_filename != null && ! log_filename.equals (name)) { System.out.println ("Multiple log files -" + NL + " " + log_filename + NL + " and" + NL + " " + name); Usage (); } if (name.length () == 0) name = STDOUT; log_filename = name; break; case 'P': // Port case 'p': if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') Usage (); try {port = Integer.parseInt (args[count]);} catch (NumberFormatException exception) { System.out.println ("Invalid port number: " + args[count]); Usage (); } break; default: System.out.println ("Unknown argument: " + args[count]); case 'H': // Help case 'h': Usage (); } } else { System.out.println ("Unknown argument: " + args[count]); Usage (); } } Configuration configuration = null; if (configuration_filename != null) { try {configuration = new Configuration (configuration_filename);} catch (IllegalArgumentException exception) { System.out.println ("Unable to find the configuration file - " + configuration_filename); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (Configuration_Exception exception) { System.out.println ("Could not load the configuration file - " + configuration_filename + NL + exception.getMessage ()); System.exit (EXIT_CONFIG_FILE_PROBLEM); } } Dispatcher dispatcher = new Dispatcher (configuration, port); if (DEBUG != DEBUG_OFF) { if (log_filename == null) log_filename = STDOUT; } if (log_filename != null) { try {System.out.println (dispatcher.Log_Pathname (log_filename));} catch (FileNotFoundException exception) { System.out.println (exception.getMessage ()); System.exit (EXIT_NO_LOG_FILE); } dispatcher.Logging (true); } try {dispatcher.Start ();} catch (Configuration_Exception exception) { dispatcher.Log_Time ("Could not load the configuration." + NL + exception.getMessage () + NL); System.exit (EXIT_CONFIG_FILE_PROBLEM); } catch (IOException exception) { dispatcher.Log_Time ("Server I/O failure:" + NL + exception.getMessage () + NL); System.exit (EXIT_IO_ERROR); } catch (SecurityException exception) { dispatcher.Log_Time ("Server security violation:" + NL + exception.getMessage () + NL); System.exit (EXIT_SECURITY_VIOLATION); } catch (Exception exception) { dispatcher.Log_Time ("Server failure:"); if (dispatcher.Logging ()) exception.printStackTrace (dispatcher.Log_Stream ()); dispatcher.Log (); System.exit (EXIT_UNKNOWN_EXCEPTION); } } public static void Usage () { System.out.println ("Usage: " + Default_Dispatcher_Name () + " " + NL +" Options -" + NL +" -Configuration " + " (default: " + Default_Configuration_Source () + ')' + NL +" -Logging []" + " (default: " + Default_Log_Filename () + ')' + NL +" -Port " + " (default: " + DEFAULT_PORT + ')' + NL +" -Help" + NL ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/PVL/0000755000175000017500000000000012052546517013562 5ustar mathieumathieupirl-2.3.8/PIRL/PVL/Makefile0000644000175000017500000000136211613716657015232 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.15 2011/07/27 05:10:07 castalia Exp # # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm # Location of SwingX Classes. SwingX ?= /opt/java/SwingX/swingx.jar # Location of the JavaMail Classes (used by Notify; cross-dependency). JavaMail ?= /opt/java/javamail/mail.jar JPATH ?= ../..:$(JCM):$(SwingX):$(JavaMail) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Parameter.class \ Selector.class \ Selection.class \ Value.class \ Parser.class \ Lister.class \ PVL_Exception.class \ Invalid_Map_Syntax.class \ PVL_to_DB.class all: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/PVL/Parameter.java0000644000175000017500000035777311742734277016402 0ustar mathieumathieu/* Parameter PIRL CVS ID: Parameter.java,v 1.44 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; // The base class import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import java.util.List; import java.util.Vector; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; import java.io.OutputStream; import java.io.StringWriter; import java.io.IOException; import PIRL.Strings.String_Buffer; /** A Parameter is a general-purpose named entity containing an arbitrary value. It is derived from the Parameter Value Language syntax:

    Name = Value

    Every Parameter has a Name which is a String of zero or more characters.

    The kind of Value that a Parameter contains is indicated by its Classification code. There are three basic classifications:

    TOKEN
    A Parameter with a name but no value.
    ASSIGNMENT
    The fundamental Parameter containing a Value object.
    AGGREGATE
    A Parameter that contains a List of zero or more Parameters. An aggregate may have a GROUP or OBJECT specific classification; a distinction without a difference for this package (but which may be useful to the application).

    There is also an UNKNOWN classification for a completely vacant Parameter (though its name is the empty string, not null). In addition, END classification Parameters may be used to mark the end of an aggregate's Parameter List without removing the Parameters beyond the end marker.

    A Parameter may have Comments associated with it that contain any descriptive text.

    @see Value @see Parser @author Bradford Castalia, UA/PIRL @version 1.44 */ public class Parameter extends DefaultMutableTreeNode implements Cloneable { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Parameter (1.44 2012/04/16 06:14:23)"; /* The Parameter's name. It is the userObject of the base class and is always a String. */ /* When a Parameter is an ASSIGNMENT, _Value_ holds its Value. However, when a Parameter is an AGGREGATE, then the children variable in the super class holds a Vector of Parameters. */ private Value _Value_ = null; private int _Classification_ = UNKNOWN; /* Parameter _Classification_ codes: There are three basic Parameter classifications: TOKEN - _Value_ is null (no value), children is null. ASSIGNMENT - _Value_ is a Value object, children is null. AGGREGATE - _Value_ is null, children is a Vector of Parameter objects. In addition there is the UNKNOWN classification for empty Parameters. The _Classification_ for an AGGREGATE Parameter provides a distinction between a GROUP and an OBJECT: with the former having "GROUP" (or "BEGIN_GROUP") as its Parameter name in the PVL syntax, and the latter having "OBJECT" (or "BEGIN_OBJECT"). They are both treated identically in this package (the distinction is application dependent). The value assigned to these special Parameters is expected to be a string which becomes the Name of the corresponding Parameter. Note that while a Parameter classified as an END_XXX Parameter may be used, they are not required except in the external PVL syntax. The appropriate END_XXX Parameter is implicit, and will be provided automatically by the Write method, at the end of each AGGREGATE. An END_XXX Parameter inserted in the Parameter list of an AGGREGATE will functionally truncate the list at that point; the additional Parameters of the list remain and can be functionally recovered by removing the END_XXX Parameter. An END_PVL Parameter, will truncate any remaining PVL Parameters in the current list and any parent aggregates lists. Note that Parameter _Classification_ codes are expected to be distinct from Value _Type_ codes. */ /** The lowest bit used by the classification code bit field. */ public static final int LOWEST_BIT = Value.HIGHEST_BIT + 1; /** The highest bit used by the classification code bit field. */ public static final int HIGHEST_BIT = LOWEST_BIT + 5; /** Classification for a Parameter with only a name. */ public static final int TOKEN = 1 << (LOWEST_BIT + 1); /** Classification for a Parameter with a Value. */ public static final int ASSIGNMENT = 1 << (LOWEST_BIT + 2); /** Non-specific classification for a Parameter associated with an aggregate list.

    This classification is a place-holder for all Parameters associated with a Parameter aggregate list. If it is used with the Classification method, the default GROUP classification will be applied.

    @see #GROUP */ public static final int AGGREGATE = 1 << (LOWEST_BIT + 3); /** Used to mark the end of an aggregate Parameter's List.

    Do not set the classification of a Parameter to this value, but logically OR it with a specific classification. For example, to temporarily mark a Parameter as the end of an aggregate:

    Parameter parameter = aggregate.Find ("Parameter_Name");
    if (parameter != null)
        {
        // Mark the end of the list here.
        parameter.Classification (parameter.Classifcation () | END);
        // The list stops with the parameter before "Parameter_Name".
        parameter.Parent ().Write ();
        // Restore the previous classification.
        parameter.Classification (parameter.Classifcation () & ~END);
        }
    

    Using the END_AGGREGATE classification may be easier.

    @see #END_AGGREGATE @see #END_PVL */ public static final int END = 1 << LOWEST_BIT; /** A special classification that marks the end of all Parameters.

    An END_PVL Parameter will truncate any remaining Parameters in the current List and the Lists of any Parent Parameters. However, the truncated Parameters remain in place and can be restored by restoring the classification of the marked Parameter.

    @see #END @see #END_AGGREGATE */ public static final int END_PVL = TOKEN | END; /** Special classification for the end of an aggregate Parameter List.

    The END_GROUP and END_OBJECT are specific versions of this classification.

    Note that while a Parameter classified as an END_XXX Parameter may be used, they are not required (and typically not used) except in the PVL syntax seen by the Parser. The appropriate END_XXX Parameter is implicit, and will be provided automatically by the Write method, at the end of each aggregate. An END_XXX Parameter in a Parameter List will functionally truncate the List at that point; the additional Parameters of the List remain and can be functionally recovered by restoring the original classification. For example,

    Parameter parameter = aggregate.Find ("Parameter_Name");
    if (parameter != null)
        {
        // Save the current classification.
        int classification = parameter.Classification ();
        // Mark the end of the list here.
        parameter.Classification (Parameter.END_AGGREGATE);
        // The list stops with the parameter before "Parameter_Name".
        parameter.Parent ().Write ();
        // Restore the previous classification.
        parameter.Classification (classification);
        }
    

    @see #END @see #END_PVL */ public static final int END_AGGREGATE = AGGREGATE | END; /** Classification for a Parameter that contains a List of Parameters.

    This Parameter has no Value, but instead contains a List (Vector) of children Parameters. This classification is functionally identical to OBJECT. The distinction is based on the PVL keyword syntax recognized by the Parser when an aggregate Parameter is constructed.

    @see Parser */ public static final int GROUP = AGGREGATE | (AGGREGATE << 1); /** Alias for GROUP.

    @see #GROUP */ public static final int BEGIN_GROUP = GROUP; /** A specific form of END_AGGREGATE.

    @see #END_AGGREGATE */ public static final int END_GROUP = GROUP | END; /** Classification for an aggregate Parameter; and is functionally identical to the GROUP classification.

    Don't confuse this classification with the Java Object class. The name is a formal keyword in the PVL syntax.

    @see #GROUP */ public static final int OBJECT = AGGREGATE | (AGGREGATE << 2); /** Alias for OBJECT.

    @see #OBJECT */ public static final int BEGIN_OBJECT = OBJECT; /** A specific form of END_AGGREGATE.

    @see #END_AGGREGATE */ public static final int END_OBJECT = OBJECT | END; /** The classification of an empty Parameter. */ public static final int UNKNOWN = 0; /** Selects only the classification code bit fields recognized by the Parameter class.

    Logical AND with another int variable will ensure that only Parameter classification bits remain. */ public static final int MASK = 0x3F << LOWEST_BIT; /* Any, and all, comment sequences preceeding the Parameter definition in the PVL syntax are collected together. */ private String _Comments_ = null; /* The delimiter for Parameter pathnames used when finding a nested Parameter by name. */ private static final char PATH_DELIMITER = '/'; private static char _Path_Delimiter_ = PATH_DELIMITER; // When throwing an exception is inappropriate. private PVL_Exception _First_Warning_ = null, _Last_Warning_ = null; private boolean _Use_First_Warning_ = true; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_NAME = 1 << 1, DEBUG_DATA = 1 << 2, DEBUG_WRITE = 1 << 3, DEBUG_FIND = 1 << 4, DEBUG_MATCH = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates an empty Parameter, with classification UNKNOWN and an empty string name (not null). */ public Parameter () { /* Don't allow a null Parameter name. A null Parameter name is used as a special flag in the find method. Parameters are referenced by their names which are used in other contexts. A Parameter with an empty ("") name would be very unlikely (though possible); but a null name string should not be possible from the external view. */ super (""); } /** Creates a token Parameter with only a name.

    @param name The name of this token. If null, the name will be set to the empty String. */ public Parameter ( String name ) { super ((name == null) ? "" : name); _Classification_ = TOKEN; allowsChildren = false; } /** Create a Parameter by getting all Parameters from a Parser.

    When the source for the Parser is a String, and the String's PVL defines more than one Parameter (excluding any Parameters that may be contained within aggregate Parameters), then an aggregate Parameter will be provided to contain all of the Parameters, and will be given the name {@link Parser#CONTAINER_NAME Parser.CONTAINER_NAME}. However, if the String only defines a single, top-level Parameter, then that is what will be provided.

    When the source for the Parser is not a String, then a container Parameter will always be used to hold the results from the Parser, regardless of the number of Parameters that are defined.

    @param parser The Parser source for PVL syntax. @throws PVL_Exception When the Parser encounters an unrecoverable error. The Parser may also generate a Warning which will associated with the new Parameter. @see Parser */ public Parameter ( Parser parser ) throws PVL_Exception { super (""); // In case the parser is null. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Parameter construction from a Parser"); if (parser != null) { // Graft the Parameter from the parser into this Parameter. graft (parser.Get ()); _First_Warning_ = _Last_Warning_ = parser.Warning (); if ((DEBUG & DEBUG_SETUP) != 0 && _First_Warning_ != null) System.out.println ("Parameter construction warning:\n" + Warning ()); } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Parameter construction from Parser completed."); } /** A Parser is used to get a new Parameter which is given the specified name.

    This constructor is typically used when the source for the Parser is a File and the resultant aggregate Parameter is to be given the name of the File, rather than the default {@link Parser#CONTAINER_NAME Parser.CONTAINER_NAME}. Note that if the Parser source is a String that defines a single Parameter, then the name of that Parameter will be overriden by the new name from this constructor.

    @param parser The Parser source for PVL syntax. If null this is the same as using the no argument constructor. @param name The name of the new Parameter. If null, the name will be set to the empty String. @throws PVL_Exception When the Parser encounters an unrecoverable error. The Parser may also generate a Warning which will be associated with the new Parameter. @see Parser */ public Parameter ( String name, Parser parser ) throws PVL_Exception { this (parser); Name (name); } /** A Parameter is constructed from a copy of another Parameter.

    The contents of the original Parameter are copied into the new Parameter. The warning status from the original is not copied.

    When the original Parameter is an assignment, the Value is completely copied.

    When the original is an aggregate, a new Parameter list is created in which each element is a recursive copy of the elements from the original list in the same order. Note: List copying stops at END_AGGREGATE parameters, and all copying stops at END_PVL parameters; the new Parameter is a logical copy of the original. Thus the new Parameter will be equivalent ({@link #equalsIgnoreCase equalsIgnoreCase}) to the original, but may not be identical ({@link #equals equals}).

    @param parameter The original Parameter to be copied. If null this is the same as using the no argument constructor. @throws PVL_Exception

    BAD_ARGUMENT
    An inappropriate object was found as the element of an aggregate Parameter list or Value array during copying.
    @see #set_classification(int) @see #listIterator () @see #Add(Parameter) @see Value#Value(Value) */ public Parameter ( Parameter parameter ) throws PVL_Exception { super (""); // In case the parameter is null. if (parameter != null) { boolean end[] = {false}; graft (replicate (parameter, end)); } } private Parameter replicate ( Parameter parameter, boolean[] end ) throws PVL_Exception { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> replicate"); Parameter replica = new Parameter (parameter.Name ()); replica._Comments_ = parameter._Comments_; // Don't copy the warning status. replica.set_classification (parameter.Classification ()); Replicate_List: if (parameter.Is_Aggregate ()) { if (parameter.children != null) { /* Use the Parameter iterator. END_XXX parameters will end the list. END_PVL parameters will be specially noticed and the end flag will be set so that no further list copying will be done up the recursion line. */ ListIterator list = parameter.listIterator (); while (list.hasNext ()) { Object data = list.next (); if (! Is_Parameter (data)) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "While constructing a new Parameter\n" + " from the " + parameter.Classification_Name () + " Parameter named \"" + parameter.Name () + "\"\n" + " element " + list.previousIndex () + " is an object of class " + data.getClass ().getName () + "." ); /* Add a replica of the Parameter from the list. This will recursively replicate any aggregate Parameters. */ if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("replicate: parameter " + (Parameter)data); replica.Add (replicate ((Parameter)data, end)); if (end[0]) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("replicate: END_PVL in sub-parameter"); break Replicate_List; } } // Test for END_PVL. try {list.next ();} catch (NoSuchElementException exception) { if (exception.getMessage () .startsWith (Classification_Name (END_PVL) + "\n")) { end[0] = true; if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("replicate: END_PVL in list"); } } } } else if (parameter.Is_Assignment ()) { if (parameter._Value_ != null) { replica._Value_ = new Value (parameter._Value_); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("replicate: Value " + _Value_); } } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Parameter: replicate"); return replica; } /*============================================================================== Accessors */ /*------------------------------------------------------------------------------ Name: */ /** Gets the name of the Parameter.

    @return The name of the Parameter. This will not be null. */ public String Name () {return (String)userObject;} /** Sets the Parameter's name.

    @param name The name String. If null the name will be set to the empty String. @return This Parameter. */ public Parameter Name ( String name ) { if ((DEBUG & DEBUG_NAME) != 0) System.out.println ("Parameter.Name = " + name); userObject = (name == null) ? "" : name; return this; } private Parameter set_name ( String name ) { // Allows setting the name to null. userObject = name; return this; } /** Gets the name of the Parameter.

    @return The name String of the Parameter. */ public String toString () {return (String)userObject;} /** Gets the fully qualified pathname of the Parameter.

    The pathname is the name of the Parameter preceeded by the current {@link #Path_Delimiter() Path_Delimiter} character. If this Parameter has a {@link #Parent() Parent} then the delimiter is preceeded by the parent's pathname. This recursion produces a pathname that starts with the delimiter character followed by the name of the root Parameter (no parent), the names of all containing aggregate Parameters leading to this Parameter separated by the delimiter, and ending with the name of this Parameter. If this Parameter has no parent, then the pathname is just its name preceeded by the delimiter.

    @return The pathname String for the Parameter. @see #Path_Delimiter() @see #Parent() */ public String Path_Name () { // The full pathname including this Parameter name. if (parent != null) return ((Parameter)parent).Path_Name () + _Path_Delimiter_ + (String)userObject; return _Path_Delimiter_ + (String)userObject; } /** Gets the pathname of the Parameter without the Parameter's name.

    The trailing {@link #Path_Delimiter() Path_Delimiter} character is included. If this Parameter has no parent, then only the delimiter will be returned.

    @return The pathname String up to, but not including, the Parameter. @see #Path_Name() */ public String Path_to_Name () { // The pathname without this Parameter name. String pathname = Path_Name (); return pathname.substring (0, pathname.lastIndexOf (_Path_Delimiter_) + 1); } /** Gets the pathname of the Parameter from some parent Parameter.

    The pathname includes the names of each Parameter from, but not including, the root_parent up to, and including, this Parameter. The pathname will begin with the {@link #Path_Delimiter() Path_Delimiter} character and each Parameter name will be separated from the next by the same delimiter.

    Note: The pathname returned by this method is the absolute pathname for the Parameter in the root_parent, as used by the Find method.

    @param root_parent A Parent of this Parameter. @return The absolute pathname to this Parameter from the root_parent, or null if the root_parent is not a parent of this Parameter. */ public String Path_from ( Parameter root_parent ) { if (parent != null && root_parent != null) { if (parent == root_parent) return _Path_Delimiter_ + (String)userObject; String pathname = ((Parameter)parent).Path_from (root_parent); if (pathname != null) return pathname + _Path_Delimiter_ + (String)userObject; } return null; } /** Gets the current Path_Name delimiter.

    The default delimiter is the slash ('/') character.

    @return The delimiter char used for constructing pathnames. */ public static char Path_Delimiter () {return _Path_Delimiter_;} /** Sets the Path_Name delimiter.

    Note: The delimiter is a static class variable that applies to all Parameters.

    @param delimiter The delimiter char. @return The previous delimiter character. */ public static char Path_Delimiter ( char delimiter ) { char previous_delimiter = _Path_Delimiter_; _Path_Delimiter_ = delimiter; return previous_delimiter; } /** Combines two pathnames to produce an absolute pathname.

    The base_path is taken to be relative to the parent_path. Typically the base_path is a simple name (e.g. a {@link #Basename Basename}), and the parent_path is a path from the root (e.g. a {@link #Parent_Pathname(String) Parent_Pathname}). However, any pathnames will do. If the parent_path is not {@link #Is_Absolute_Pathname(String) absolute} it is made absolute. The base_path, if not null or empty, is appended to the parent_path with a single separating delimiter.

    @param parent_path The pathname of the parent parameter. If null or empty, the root pathname is used. @param base_path The pathname of the parameter relative to the parent_path. @return The absolute pathname. */ public static String Absolute_Pathname ( String parent_path, String base_path ) { StringBuffer absolute_path = new StringBuffer (); char delimiter = Path_Delimiter (); absolute_path.append (delimiter); // Find the first and last non-delimiter characters. int first = 0, last = 0; if (parent_path != null) { last = parent_path.length (); for (first = 0; first < last && parent_path.charAt (first) == delimiter; first++); for (last--; last >= 0 && parent_path.charAt (last) == delimiter; last--); last++; } if (first < last) absolute_path.append (parent_path.substring (first, last)); // Find the first non-delimiter character. first = -1; if (base_path != null) { last = base_path.length (); for (first = 0; first < last && base_path.charAt (first) == delimiter; first++); if (first == last) first = -1; } if (first >= 0) { if (absolute_path.length () > 1) absolute_path.append (delimiter); absolute_path.append (base_path.substring (first)); } return absolute_path.toString (); } /** Produce an absolute pathname.

    The specified pathname is converted, if needed, to a top level absolute pathname.

    @param pathname The pathname of a parameter. @return The absolute pathname. */ public static String Absolute_Pathname ( String pathname ) {return Absolute_Pathname (null, pathname);} /** Test if a pathname is absolute.

    An absolute pathname begins with the current {@link #Path_Delimiter () path delimiter}.

    @param pathname The pathname of a parameter. @return true if the pathname is absolute; false otherwise. A null or empty pathname is not absolute. */ public static boolean Is_Absolute_Pathname ( String pathname ) { return pathname != null && pathname.length () > 0 && pathname.charAt (0) == Path_Delimiter (); } /** Gets the portion of a pathname to the parent parameter.

    The parent pathname is the substring of the pathname preceding the last {@link Parameter#Path_Delimiter() path delimiter} (usually the '/' character). If there is no delimiter in the pathname, or nothing preceding the last delimiter, then there is no parent pathname.

    N.B.: This function only considers the specified pathname. It does not provide the name of a Parameter's {@link #Parent() parent}.

    @param pathname The String naming a pathname to a parameter. @return The String naming the pathname to the parent parameter, or null if the parameter has no parent (or the specified pathname is null). */ public static String Parent_Pathname ( String pathname ) { if (pathname != null) { int index = pathname.lastIndexOf (Path_Delimiter ()); if (index > 0) return pathname.substring (0, index); } return null; } /** Gets the basename portion of the pathname.

    The basename is the substring of the pathname following the last path {@link Parameter#Path_Delimiter() delimiter} (usually the '/' character). If the pathname does not contain a delimiter, then the entire pathname is the basename.

    @param pathname The String naming a pathname to a parameter. @return The basename substring of the pathname. This will be null if the specified pathname is null. It will be the empty String if the pathname ends with the path delimiter. */ public static String Basename ( String pathname ) { if (pathname == null) return null; int index = pathname.lastIndexOf (Path_Delimiter ()); if (index < 0) return pathname; return pathname.substring (++index); } /** Gets a PVL description of the Parameter using a specified Lister.

    If no Lister is specified (the argument is null) then a default Lister will be provided.

    The Lister will have its Writer {@link Lister#Set_Writer(Writer) set} to a StringWriter used to write the PVL syntax. The Parameter is {@link #Write(OutputStream) Write}n using the Lister and the resultant String, without any final end-of-line sequence, is returned. If the Write method produces an exception, the exception message is returned after a {@link Lister#NEW_LINE} (this should be a unique flag that producing the PVL description failed).

    @param lister The Lister to use when writing the PVL syntax. @return The String describing the Parameter in PVL syntax. */ public String Description ( Lister lister ) { String description = null; StringWriter writer = new StringWriter (); if (lister == null) lister = new Lister (writer); else lister.Set_Writer (writer); try { lister.Write (this); description = writer.toString (); if (description.length () != 0) { String EOL = lister.Strict () ? Parser.LINE_BREAK : Lister.NEW_LINE; if (description.endsWith (EOL)) description = description.substring (0, description.length () - EOL.length ()); } } catch (Exception exception) {description = Lister.NEW_LINE + exception.getMessage ();} return description; } /** Gets a PVL description of the Parameter.

    The Parameter is {@link #Write(OutputStream) Write}n to a StringWriter on a default Lister and the resultant String is returned. If the Write method produces an exception, the exception message is returned.

    @return The String describing the Parameter in PVL syntax. @see #Description(Lister) */ public String Description () {return Description (null);} /*------------------------------------------------------------------------------ Value: */ /*.............................................................................. Get */ /** Gets the Value assigned to the Parameter.

    @return The Parameter's Value. If the Parameter has no Value, null is returned. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Parameter is not an assignment or token.
    */ public Value Value () throws PVL_Exception { if (Is_Assignment ()) return _Value_; if (Is_Token ()) return null; throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a Value from the " + Classification_Name () + " Parameter named \"" + Name () + "\"." ); } /** Gets the Parameter list aggregated under this Parameter.

    @return The Parameter's list as a Vector object. If the Parameter has no Parameter list, null is returned. If the Parameter has no list because it is not classified as an aggregate, then a PVL_Exception.ILLEGAL_SYNTAX warning status is set. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Parameter is not an aggregate.
    */ public Vector List () throws PVL_Exception { if (Is_Aggregate ()) return children; throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a Parameter List from the " + Classification_Name () + " Parameter named \"" + Name () + "\"." ); } /** Determines if the Parameter list is empty.

    @return true if the Parameter list is empty, or the Parameter has no list (a Parameter that is not classified as an aggregate will have no list). Otherwise, false. @see Vector#isEmpty() */ public boolean Is_Empty () { if (Is_Aggregate () && children != null) return children.isEmpty (); return true; } /** Get the Parameter's data object.

    @return The Parameter's data object. If the Parameter is classified as an aggregate, this will be the Parameter list (Vector). Otherwise it will be the Parameter's Value. In either case, the object may be null. */ public Object Data () { if (Is_Aggregate ()) return children; return _Value_; } /*.............................................................................. Set */ /** Sets the Value for the Parameter.

    The Parameter is classified as an assignment and the specified Value is assigned. If the Parameter previously had data, it is replaced. In the case where previous data was an aggregate Parameter list, all elements of the list are orphaned (they are marked as having no parent). Note: The Value is not copied.

    @param value The Value object to be assigned. @return This Parameter. @see #set_classification(int) @see #set_data(Object) */ public Parameter Value ( Value value ) { set_classification (ASSIGNMENT); try {set_data (value);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Assigns a new Value constructed from a primitive byte value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (byte value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from a primitive short value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (short value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from a primitive int value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (int value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from a primitive long value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (long value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from a primitive float value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (float value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from a primitive double value.

    @param value The primitive datum for the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value (double value) throws PVL_Exception {return Value (new Value (value));} /** Assigns a new Value constructed from an Object.

    @param value The Object from which to construct the new Value. @return This Parameter. @throws PVL_Exception If a new Value could not be constructed from the value. @see #Value(Value) */ public Parameter Value ( Object value ) throws PVL_Exception { if (value == null) return Data (value); return Value (new Value (value)); } /** Sets the Parameter list for the Parameter.

    The Parameter is classified as an AGGREGATE (i.e. GROUP) and the contents of the Vector are adopted. The new list must contain only Parameter elements, each of which has its parent set to this Parameter. If the Parameter previously had data, it is replaced. In the case where previous data was an aggregate Parameter list, all elements of the list are orphaned (they are marked as having no parent). Note: The list is not copied.

    @param list The Vector of Parameters to be adopted. @return This Parameter. @throws PVL_Exception From set_data. @see #set_classification(int) @see #set_data(Object) */ public Parameter List ( Vector list ) throws PVL_Exception { set_classification (GROUP); set_data (list); return this; } /** Gets the size of an aggregate's list; the number of Parameters it contains.

    @return The number of Parameters in the aggregate list. If the Parameter is not an aggregate or has no list, zero will be returned; i.e. returning zero is not determinate that the Parameter is an aggregate with an empty list. N.B.: This value does not include the number of Parameters in any aggregates in this Parameters list. */ public int List_Size () { if (Is_Aggregate () && children != null) return children.size (); return 0; } /** Copies the contents of a Parameter into this Parameter.

    This Parameter is made equivalent ({@link #equalsIgnoreCase(Parameter) equalsIgnoreCase} not necessarily {@link #equals(Object) equals}) to the specified Parameter.

    @param parameter The Parameter to be copied into this Parameter. If null, and empty parameter will be used. @return This Parameter. @throws PVL_Exception From graft. @see #Parameter(Parameter) @see #graft(Parameter) */ public Parameter Data ( Parameter parameter ) throws PVL_Exception { // Graft a deep copy of the original Parameter. graft (new Parameter (parameter)); return this; } /** Sets the data for the Parameter.

    If the data is a Parameter object then its contents are copied into this Parameter; i.e. this Parameter is made equivalent ({@link #equalsIgnoreCase(Parameter) equalsIgnoreCase} not necessarily {@link #equals(Object) equals}) to the specified Parameter. Otherwise the Parameter is classified based on the {@link #Classification Classification} of the data Object specified. The classification will be an ASSIGNMENT if the data is a Value, an AGGREGATE (i.e. GROUP) for Parameter list data, or a TOKEN if the data is null (unless the Parameter has no name, in which case the classification is UNKNOWN). The data is applied to the Parameter appropriately as if either the {@link #Value(Value) Value (Value)} or {@link #List(Vector) List (Vector)} method were used.

    @param data The data Object to be applied. @return This Parameter. @throws PVL_Exception From set_data. @see #Data(Parameter) @see #Classification(Object) @see #set_classification(int) @see #set_data(Object) @see #Value(Value) @see #List(Vector) */ public Parameter Data ( Object data ) throws PVL_Exception { if (Is_Parameter (data)) return Data ((Parameter)data); int classification = Classification (data); if (classification == AGGREGATE) classification = GROUP; // Be specific. set_classification (classification); if (data == null && Name () != null && Name ().length () != 0) set_classification (TOKEN); // Instead of UNKNOWN. set_data (data); return this; } /*.............................................................................. Modify */ /** Inserts a Parameter into the aggregate list at the specified index.

    If the Parameter is not already classified as an aggregate, and does not have a Value assigned, it is given the GROUP classification. The specified Parameter is inserted at the specified index in the aggregate Parameter list. Any Parameters in the list at, or beyond the index are moved to the next index.

    After the specified Parameter is inserted its parent is set to this Parameter.

    Warning: Inserted parameters are not removed from any Aggregate list they may already be in.

    @param parameter The Parameter object to be inserted. @param index The location in the list for the insertion. @return This Parameter. @throws PVL_Exception

    ILLEGAL_SYNTAX
    The Parameter is an assignment with a Value.
    @see #Classification(int) @see #Remove(Parameter) @see Vector#insertElementAt(Object, int) */ public Parameter Insert ( Parameter parameter, int index ) throws PVL_Exception { if (parameter == null) return this; if ((DEBUG & DEBUG_DATA) != 0) System.out.println (">>> Parameter.Insert: " + parameter.Name () + " into " + Name () + " at index " + index); if (Is_Assignment () && _Value_ != null) throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't Insert the Parameter named \"" + parameter.Name () + "\"\n" + " into the " + Classification_Name () + " Parameter named \"" + Name () + "\"." ); if (! Is_Aggregate ()) Classification (GROUP); // Adopt it as one of the children. if (children == null) children = new Vector (); try {children.insertElementAt (parameter, index);} catch (RuntimeException exception) { throw new PVL_Exception ( ID, PVL_Exception.SYSTEM_ERROR, "During Insert at index " + index + " of the Parameter named \"" + parameter.Name () + "\"\n" + exception.getMessage () ); } parameter.parent = this; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Parameter.Insert"); return this; } /** Inserts a List of Parameters into the aggregate list starting at the specified index.

    The elements of the List are Inserted in the order they occur from the List's listIterator at sequential locations starting with the specified index.

    @param list The List of Parameters to Insert. @param index The first of sequential Insert locations. @return This Parameter. @throws PVL_Exception

    BAD_ARGUMENT
    An element of the List is not a Parameter.
    @see #Insert(Parameter, int) @see List#listIterator() */ public Parameter Insert ( List list, int index ) throws PVL_Exception { if (list != null) { ListIterator parameter_list = list.listIterator (); while (parameter_list.hasNext ()) { Object object = parameter_list.next (); if (! Is_Parameter (object)) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "During Insert at index " + index + " of a Parameter List\n" + " to the " + Classification_Name () + " Parameter named \"" + Name () + "\"\n" + " List element " + parameter_list.previousIndex () + " is an object of class " + object.getClass ().getName () + "." ); Insert ((Parameter)object, index++); } } return this; } /** Adds a Parameter to the end of the aggregate list.

    Uses the Insert method where the index is the end of the list.

    @param parameter The Parameter to be added. @return This Parameter. @throws PVL_Exception From Insert. @see #Insert(Parameter, int) */ public Parameter Add ( Parameter parameter ) throws PVL_Exception {return Insert (parameter, getChildCount ());} /** Adds a List of Parameters to the end of the aggregate list.

    Performs an {@link #Insert(List, int) Insert} where the index is the end of the list.

    @param list The List of Parameters to be added. @return This Parameter. @throws PVL_Exception From Insert. @see #Insert(List, int) */ public Parameter Add ( List list ) throws PVL_Exception {return Insert (list, getChildCount ());} /** Adds the Parameters obtained from a Parser to this Parameter's aggregate list.

    @param parser The Parser to use as the source of PVL statements. @return This Parameter. @throws PVL_Exception From the Parser. Note: The warning status for the Parser is cleared before it is called, and then applied to this Parameter afterwards. @see Parser#Add_To(Parameter) @see Parser#Warning() */ public Parameter Add ( Parser parser ) throws PVL_Exception { parser.Reset_Warning (); // Start clean. parser.Add_To (this); Warning (parser.Warning ()); return this; } /** Removes the Parameter at the specified index from the aggregate list.

    After being removed from the aggregate list the Parameter is marked as having no parent.

    @param index The aggregate list index of the Parameter to be removed. @return The Parameter removed. If this Parameter is not an aggregate, or has no list, null is returned. If this Parameter is not an aggregate a PVL_Exception.ILLEGAL_SYNTAX Warning status is set. @see Vector#remove(int) */ public Parameter Remove ( int index ) { Parameter removed = null; if (! Is_Aggregate ()) Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't Remove from the " + Classification_Name () + " Parameter named \"" + Name () + "\"." ); if (children != null) { // Remove the entry from the Parameter list. removed = (Parameter)children.remove (index); // Make it an orphan. removed.parent = null; } return removed; } /** Removes the specified Parameter from the aggregate list.

    The first occurance of the Parameter in the list is removed. The aggregate list, if there is one, is searched regardless of the classification of this Parameter. Only this Parameter list is searched, not the lists of any aggregate Parameters in the list. The Parameter is found in the aggregate list by equality of Parameter object reference, not by the equals method. To remove a Parameter that equals another Parameter, use Find:

    // Use Path_from only if the specific location matters.
    parameter = aggregate.Find (parameter.Name (parameter.Path_from (aggregate)));
    if (parameter != null)
        parameter.Parent ().Remove (parameter);
    

    @param parameter The Parameter object to be removed. If null nothing is done and null is returned. @return The Parameter removed. If this Parameter is not an aggregate, or has no list, null is returned. If this Parameter is not an aggregate an ILLEGAL_SYNTAX Warning status is produced. @see #Remove(int) */ public Parameter Remove ( Parameter parameter ) { Parameter removed = null; if (parameter != null && children != null) { for (int index = 0; index < children.size (); index++) { if ((removed = (Parameter)children.get (index)) == parameter) { // Remove the entry from the Parameter list. children.remove (index); // Make it an orphan. removed.parent = null; } } } return removed; } /** Empties the Parameter aggregate list.

    The aggregate list, if it exists, is removed regardless of the classification of this Parameter. All Parameters in the list (but not sub-lists) are marked as not having a parent.

    @return This Parameter. */ public Parameter Remove_All () { orphan (children); children = null; return this; } /*.............................................................................. */ /** Set the appropriate Data variables.

    The Classification of the data object determines how the internal data variables are set. Consistency of the Parameter's data is ensured by always setting it with this method.

    For Vector data, the data becomes the base class aggregate list (children) Vector. Its contents are adopted which confirms that each element is a Parameter and its parent is set to this Parameter. This Parameter's Value is set to null.

    For Value data, the data becomes the Value of the Parameter. The aggregate list (children) contents are orphaned and then the children Vector is set to null.

    >>> WARNING <<< The classification of the Parameter is not updated. Accordingly, this method should only be used by methods that will also set the classification appropriately.

    @param data The Object to become the Parameter's data. @throws PVL_Exception

    BAD_ARGUMENT
    If the data object is not a valid class for a Parameter (null is acceptable), or the data object is a Vector and one of its elements is not a Parameter.
    @see #Classification(Object) @see #adopt(Vector) @see #orphan(List) */ protected void set_data ( Object data ) throws PVL_Exception { switch (Classification (data)) { case UNKNOWN: // The data is null. case ASSIGNMENT: orphan (children); children = null; _Value_ = (Value)data; break; case AGGREGATE: /* N.B.: Each DefaultMutableTreeNode contains a parent reference which must be updated when it is moved into a new parent. So transfering a Parameter list into this Parameter requires setting the parent of each Parameter in the list. */ children = (Vector)data; adopt (children); _Value_ = null; break; default: throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't set_data of a Parameter to an object of class " + data.getClass ().getName () + "." ); } } /** Grafts the contents of a Parameter into this Parameter.

    In effect, this Parameter becomes the specified Parameter.

    The Value reference is set (not copied) to the specified Parameter's Value reference along with the name, comments, and warning status. The aggregate list (children) Vector reference is set to the specified Parameter's Vector and if it has contents they are adopted by this Parameter (the specified Parameter's children Vector is set to null to avoid any possible confusion). The classification is set to be the same as the specified Parameter. Note: The parent of this Parameter remains unchanged; i.e. the contents of are replaced but the context stays the same.

    @param parameter The Parameter object to graft into this Parameter. If null, nothing is done. @throws PVL_Exception From adopt. @see #adopt(Vector) @see #set_classification(int) */ protected void graft ( Parameter parameter ) throws PVL_Exception { if (parameter != null) { // Graft the contents of the Parameter into this Parameter. userObject = parameter.userObject; _Value_ = parameter._Value_; if ((children = parameter.children) != null) { // Become the parent of the children. parameter.children = null; adopt (children); } set_classification (parameter._Classification_); _Comments_ = parameter._Comments_; _First_Warning_ = parameter._First_Warning_; _Last_Warning_ = parameter._Last_Warning_; _Use_First_Warning_ = parameter._Use_First_Warning_; } } /** Sets the parents of a Vector of Parameters to this Parameter.

    >>> WARNING <<< Only the parent references for the Parameters in the Vector are set to this Parameter. The Vector itself may still be referenced by some other Parameter. In this case the adoption of the Vector will not be complete until the reference in the former parent is changed (e.g. to null).

    @param list The Vector of Parameters to adopt. @throws PVL_Exception

    BAD_ARGUMENT
    If an entry in the list is not a Parameter.
    */ protected void adopt ( Vector list ) throws PVL_Exception { Object parameter; if (list != null) { for (int index = list.size (); --index >= 0;) { if (Is_Parameter (parameter = list.get (index))) // Set the child's parent to this Parameter. ((Parameter)parameter).parent = this; else throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't adopt an object from a List\n" + " into the Parameter named \"" + Name () + "\"\n" + " element " + index + " is an object of class " + parameter.getClass ().getName () + "." ); } } } /** Sets a list of Parameters to have no parent.

    >>> WARNING <<< Only the parent references for the Parameters in the list are set to null. The list itself may still be referenced by some other Parameter. In this case the orphaning of the Vector will not be complete until the reference in the former parent is changed (e.g. to null).

    @param list The List of Parameters to orphan. */ static protected void orphan ( List list ) { Object parameter; if (list != null) for (int index = list.size (); --index >= 0;) if (Is_Parameter (parameter = list.get (index))) ((Parameter)parameter).parent = null; } /*------------------------------------------------------------------------------ Classification: */ /** Gets the Parameter's classification code.

    @return The classification code. */ public int Classification () {return _Classification_;} /** Determine the general classification of an Object.

    The Object is considered to be potential Parameter data and is tested to see if it is an instance of an acceptable Java Class for Parameter data. Note that only a general classification can be determined by examining an Object.

    @param object The Object to be tested. @return The basic classification code category int that would be associated with a Parameter having the object for its data. If the object is a Value, then the ASSIGNMENT classification is returned; if a Vector, then the AGGREGATE code is returned; and if the argument is null then the UNKNOWN classification is returned. If an invalid object is specified, then the return value is -1. */ public static int Classification ( Object object ) { if (object instanceof Value) return ASSIGNMENT; else if (object instanceof Vector) return AGGREGATE; else if (object == null) return UNKNOWN; return -1; } /** Sets the classification of the Parameter.

    Note: Changing to a new classification may result in loss of data if the Parameter's previous data is invalid under the new classification. If the new classification is not for assignment, then any Value is dropped; if the new classification is not for an aggregate, then any existing list is removed. However, END_XXX classifications are exempt: END_XXX classification Parameters are normally implicit at the end of aggregate Parameter lists. They are used explicitly to mark, probably temporarily, the end of a list or an entire hierarchy (END_PVL). Therefore, when these classifications are applied to a Parameter its underlying data is left intact.

    @param class_code The new Parameter classification int. @return This Parameter. @see #END_AGGREGATE @see #END_PVL @see #set_classification(int) @see #Remove_All() */ public Parameter Classification ( int class_code ) { if (class_code == AGGREGATE) class_code = GROUP; set_classification (class_code); if (! Is_End (class_code)) { if (! Is_Assignment (class_code)) _Value_ = null; if (! Is_Aggregate (class_code)) Remove_All (); } return this; } /** Sets the appropriate classification variables.

    Consistency of the Parameter's classification is ensured by always setting it with this method. If the classification code is for an AGGREGATE, then the allowsChildren variable of the DefaultMutableTreeNode base class will be set to true; otherwise it will be set to false.

    >>> WARNING <<< The data of the Parameter is not checked for consistency with the classification. Accordingly, this method should only be used by methods that will also set the data appropriately.

    @param class_code The int classification code for the Parameter. */ protected void set_classification ( int class_code ) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("Parameter.set_classification: For " + Name () + " to " + Classification_Name(class_code)); _Classification_ = class_code & MASK; if (Is_Aggregate ()) { allowsChildren = true; if (_Classification_ == AGGREGATE) // Force a specific classification. _Classification_ = GROUP; } else allowsChildren = false; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("Parameter.set_classification: allowsChildren = " + allowsChildren); } /** Gets the name String associated with the classification code.

    Classification names are identical to the names of the classification code constants, except that the BEGIN_XXX aliases are not used.

    @param class_code The classification code int. @return The appropriate classification name String. An invalid classification code will return a null. */ public static String Classification_Name ( int class_code ) { switch (class_code) { case ASSIGNMENT: return "ASSIGNMENT"; case AGGREGATE: return "AGGREGATE"; case END_AGGREGATE: return "END_AGGREGATE"; case GROUP: return "GROUP"; case END_GROUP: return "END_GROUP"; case OBJECT: return "OBJECT"; case END_OBJECT: return "END_OBJECT"; case UNKNOWN: return "UNKNOWN"; case TOKEN: return "TOKEN"; case END: case END_PVL: return "END"; default: return null; } } /** Gets the name String associated with the Parameter's classification.

    @return The appropriate classification name String. */ public String Classification_Name () {return Classification_Name (_Classification_);} /*.............................................................................. Parameter checkers: */ /** Tests if an Object is a Parameter.

    @param object The Object to be tested. @return true if the object is an instance of the Parameter class; otherwise false (also false if the object is null). */ public static boolean Is_Parameter (Object object) {return (object instanceof Parameter);} /** Tests if an Object is a Value suitable for a Parameter.

    @param object The Object to be tested. @return true if the object is an instance of the Value class; otherwise false (also false if the object is null). @see Value#Is_Value(Object) */ public static boolean Is_Parameter_Value (Object object) {return Value.Is_Value (object);} /** Tests if an Object is a List (Vector) suitable for an aggregate Parameter.

    Note: The contents of the list are recursively checked to all be Parameters. Aggregates without lists are acceptable Parameters.

    @param object The Object to be tested. @return true if the object is an instance of the Vector class and all its elements are Parameters; otherwise false (also false if the object is null, though a null list is acceptable [hmmm]). @see #Is_Parameter(Object) @see #Is_Begin_Aggregate() */ public static boolean Is_Parameter_List (Object object) { if (object instanceof Vector) { Object parameter, list; for (int index = ((Vector)object).size (); --index >= 0;) { if (! Is_Parameter (parameter = ((Vector)object).get (index))) return false; if (((Parameter)parameter).Is_Begin_Aggregate () && (((Parameter)parameter).children != null) && ! Is_Parameter_List (((Parameter)parameter).children)) return false; } return true; } return false; } /*.............................................................................. Classification checkers: */ /** Tests if the Parameter is classified as a token.

    @return true if the Parameter is a token. @see #TOKEN */ public boolean Is_Token () {return ((_Classification_ & TOKEN) != 0);} /** Tests if the Parameter is one of the END classifications.

    END classifcations include both the end of aggregates ({@link #END_GROUP END_GROUP} and {@link #END_OBJECT END_OBJECT}) and the end the entire hierarchy ({@link #END_PVL END_PVL})

    @return true if the the Parameter has an END classification. @see #END */ public boolean Is_End () {return ((_Classification_ & END) != 0);} /** Tests if the Parameter is the END_PVL classification.

    @return true if the Parameter has the END_PVL classification. @see #END_PVL */ public boolean Is_End_PVL () {return (_Classification_ == END_PVL);} /** Tests if the Parameter is classified as an assignment.

    An assignment Parameter does not necessarily have a Value (it may be null).

    @return true if the Parameter is a Value assignment. @see #ASSIGNMENT */ public boolean Is_Assignment () {return (_Classification_ == ASSIGNMENT);} /** Tests if the Parameter has a valid Value assigment.

    @return true if the Parameter has a Value assignment. @see #Is_Assignment() */ public boolean Has_Value () {return (Is_Assignment () && _Value_ != null && Value.Is_Value (_Value_));} /** Tests if the Parameter is classified as an aggregate.

    An aggregate Parameter does not necessarily have a aggregate list (it may be null). Aggregate classifications include {@link #GROUP GROUP} (and its alias BEGIN_GROUP), {@link #OBJECT OBJECT} (and its alias BEGIN_OBJECT), and the END_GROUP and END_OBJECT forms.

    @return true if the Parameter has an aggregate classification. @see #AGGREGATE */ public boolean Is_Aggregate () {return ((_Classification_ & AGGREGATE) != 0);} /** Tests if the Parameter has an aggregate list.

    The contents of the list are not checked (use {@link #Is_Parameter_List(Object) Is_Parameter_List} to check the list contents).

    @return true if the Parameter has an aggregate list. @see #Is_Aggregate() */ public boolean Has_List () {return (Is_Aggregate () && children != null);} /** Tests if the Parameter is classified as an aggregate beginning.

    An aggregate beginning is the Parameter that contains a list of Parameters (the aggregate list, which may be empty or null); i.e. it "begins" an aggregate list. Begin aggregate classifications include {@link #GROUP GROUP} (and its alias BEGIN_GROUP) and {@link #OBJECT OBJECT} (and its alias BEGIN_OBJECT).

    @return true if the Parameter begins (contains) an aggregate. @see #AGGREGATE */ public boolean Is_Begin_Aggregate () {return (((_Classification_ & AGGREGATE) != 0) && ((_Classification_ & END) == 0));} /** Tests if the Parameter is classified as an aggregate ending.

    An aggregate ending is the Parameter that ends a list of Parameters (the aggregate list). End aggregate classifications include {@link #GROUP END_GROUP} and {@link #OBJECT END_OBJECT}.

    @return true if the Parameter ends an aggregate list. @see #END_AGGREGATE */ public boolean Is_End_Aggregate () {return (((_Classification_ & AGGREGATE) != 0) && ((_Classification_ & END) != 0));} /** Tests if the Parameter is classified as an object aggregate.

    The {@link #OBJECT OBJECT} classification is identical to the {@link #OBJECT BEGIN_OBJECT} classification.

    @return true if the Parameter is an object aggregate. */ public boolean Is_Object () {return (_Classification_ == OBJECT);} /** Tests if the Parameter is classified as beginning an object aggregate.

    The {@link #BEGIN_OBJECT BEGIN_OBJECT} classification is identical to the {@link #OBJECT OBJECT} classification.

    @return true if the Parameter begins an object aggregate. */ public boolean Is_Begin_Object () {return (_Classification_ == BEGIN_OBJECT);} /** Tests if the Parameter is classified as ending an object aggregate list.

    @return true if the Parameter ends an object aggregate. @see #Is_End_Aggregate() */ public boolean Is_End_Object () {return (_Classification_ == END_OBJECT);} /** Tests if the Parameter is classified as a group aggregate.

    The {@link #GROUP GROUP} classification is identical to the {@link #OBJECT BEGIN_GROUP} classification.

    @return true if the Parameter is a group aggregate. */ public boolean Is_Group () {return (_Classification_ == GROUP);} /** Tests if the Parameter is classified as beginning a group aggregate.

    The {@link #BEGIN_GROUP BEGIN_GROUP} classification is identical to the {@link #GROUP GROUP} classification.

    @return true if the Parameter begins a group aggregate. */ public boolean Is_Begin_Group () {return (_Classification_ == BEGIN_GROUP);} /** Tests if the Parameter is classified as ending a group aggregate list.

    @return true if the Parameter ends a group aggregate. @see #Is_End_Aggregate() */ public boolean Is_End_Group () {return (_Classification_ == END_GROUP);} /** Tests if the Parameter is classified as UNKNOWN (an empty Parameter).

    @return true if the Parameter is an UNKNOWN classification. @see #UNKNOWN */ public boolean Is_Unknown () {return (_Classification_ == UNKNOWN);} // Static versions: /** Tests for the token classification.

    @param class_code The classification code int. @return true if the class_code is for a token. @see #TOKEN */ public static boolean Is_Token (int class_code) {return ((class_code & TOKEN) != 0);} /** Tests for an END classification.

    @param class_code The classification code int. @return true if the class_code is for an END classification. @see #Is_End() @see #END_AGGREGATE @see #END_PVL */ public static boolean Is_End (int class_code) {return ((class_code & END) != 0);} /** Tests for the END_PVL classification.

    @param class_code The classification code int. @return true if the class_code is the END_PVL classification. @see #END_PVL */ public static boolean Is_End_PVL (int class_code) {return (class_code == END_PVL);} /** Tests for the assignment classification.

    @param class_code The classification code int. @return true if the class_code is for an assignment. @see #ASSIGNMENT */ public static boolean Is_Assignment (int class_code) {return (class_code == ASSIGNMENT);} /** Tests for an aggregate classification.

    @param class_code The classification code int. @return true if the class_code is for an aggregate classification. @see #Is_Aggregate() */ public static boolean Is_Aggregate (int class_code) {return ((class_code & AGGREGATE) != 0);} /** Tests for an aggregate beginning classification.

    @param class_code The classification code int. @return true if the class_code is for an aggregate beginning. @see #Is_Begin_Aggregate() */ public static boolean Is_Begin_Aggregate (int class_code) {return (((class_code & AGGREGATE) != 0) && ((class_code & END) == 0));} /** Tests for an aggregate ending classification.

    @param class_code The classification code int. @return true if the class_code is for the end of an aggregate list. @see #Is_End_Aggregate() @see #END_AGGREGATE */ public static boolean Is_End_Aggregate (int class_code) {return (((class_code & AGGREGATE) != 0) && ((class_code & END) != 0));} /** Tests for the object aggregate classification.

    @param class_code The classification code int. @return true if the class_code is for an object aggregate. @see #Is_Object() */ public static boolean Is_Object (int class_code) {return (class_code == OBJECT);} /** Tests fpr the begin object aggregate classification.

    @param class_code The classification code int. @return true if the class_code is for a begin object aggregate. @see #Is_Begin_Object() */ public static boolean Is_Begin_Object (int class_code) {return (class_code == BEGIN_OBJECT);} /** Tests for the end object aggregate list classification.

    @param class_code The classification code int. @return true if the class_code is for the end of an object aggregate list. @see #Is_End_Object() */ public static boolean Is_End_Object (int class_code) {return (class_code == END_OBJECT);} /** Tests for the group aggregate classification.

    @param class_code The classification code int. @return true if the class_code is for a group aggregate. @see #Is_Group() */ public static boolean Is_Group (int class_code) {return (class_code == GROUP);} /** Tests for the begin group aggregate classification.

    @param class_code The classification code int. @return true if the class_code is for a begin group aggregate. @see #Is_Begin_Group() */ public static boolean Is_Begin_Group (int class_code) {return (class_code == BEGIN_GROUP);} /** Tests for the end group aggregate list classification.

    @param class_code The classification code int. @return true if the class_code is for the end of a group aggregate list. @see #Is_End_Group() */ public static boolean Is_End_Group (int class_code) {return (class_code == END_GROUP);} /** Tests for the UNKNOWN classification.

    @param class_code The classification code int. @return true if the class_code is the UNKNOWN classification. @see #UNKNOWN */ public static boolean Is_Unknown (int class_code) {return (class_code == UNKNOWN);} /*------------------------------------------------------------------------------ Comments: */ /** Gets the comments String.

    @return The String of comments associated with the Parameter. This will be null if the Parameter has no comments. */ public String Comments () {return _Comments_;} /** Sets the comments String.

    @param comments The String to become the Parameter comments. This may be null to remove all comments. @return This Parameter. */ public Parameter Comments ( String comments ) { _Comments_ = comments; return this; } /*------------------------------------------------------------------------------ Parent: */ /** Gets the Parameter that is this Parameter's parent.

    A parent Parameter is always an aggregate that contains this Parameter in its aggregate list.

    @return The parent aggregate Parameter that contains this Parameter, or null if this Parameter has no parent. */ public Parameter Parent () {return (Parameter)getParent ();} /*------------------------------------------------------------------------------ Warning: */ /** Gets the current warning status.

    When conditions are encountered that are unusual enough to warrant attention, but not an error condition that would prevent successful processing which would cause an exception to be thrown, a warning condition is registered. The warning is in the form of a PVL_Exception that was not thrown. The current warning status is either the {@link #First_Warning(boolean) First_Warning} or the {@link #Last_Warning(boolean) Last_Warning} since a {@link #Reset_Warning() Reset_Warning}.

    @return The current warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #First_Warning(boolean) @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Warning () { if (_Use_First_Warning_) return _First_Warning_; return _Last_Warning_; } /** Clears any warning status so that the Warning method will return null until the next warning condition occurs.

    @return This Parameter. @see #Warning() */ public Parameter Reset_Warning () { _First_Warning_ = null; _Last_Warning_ = null; return this; } /** Enables or disables returning the first warning that occurs as the current warning status.

    The first warning is one that occurs when the current warning status is null.

    @param first true to enable returning the first warning status; false to return the last warning that occurred as the current warning status. @return This Parameter. @see #Warning() @see #First_Warning() @see #Reset_Warning() */ public Parameter First_Warning ( boolean first ) { _Use_First_Warning_ = first; return this; } /** Returns the first warning since the last Reset_Warning.

    @return The first warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #First_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception First_Warning () {return _First_Warning_;} /** Enables or disables returning the last warning that occurs as the current warning status.

    The last warning is the most recent one regarless of any previous warning conditions that may have occured without an intervening Reset_Warning.

    @param last true to enable returning the last warning status; false to return the first warning condition that occurred as the current warning status. @return This Parameter. @see #Warning() @see #Last_Warning() @see #Reset_Warning() */ public Parameter Last_Warning ( boolean last ) { _Use_First_Warning_ = ! last; return this; } /** Returns the last warning since a Reset_Warning.

    @return The last warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Last_Warning () {return _Last_Warning_;} /*============================================================================== General Methods */ /** Makes a deep copy of the Parameter.

    @return An Object of class Parameter that is a copy of this Parameter. @see #Parameter(Parameter) @see Object#clone() @see Cloneable */ public Object clone () { // Make a deep copy. try {return new Parameter (this);} catch (PVL_Exception execption) {return null;} } /** Tests if a Parameter matches this Parameter using the specified criteria.

    @param parameter The Parameter to be compared to this Parameter. @param criteria The Selector providing the comparison criteria. @return true if there is a criteria match; false otherwise. @see Selector @see Selection */ public boolean Match ( Parameter parameter, Selector criteria ) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (">>> Parameter.Match: " + Name () + " == " + parameter); boolean match = criteria.Parameters_Match (this, parameter); if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Parameter.Match: " + match); return match; } /** Tests if a Parameter matches this Parameter using the specified criteria.

    If the Parameter is an Aggregate matching is done recursively on all Parameters of the Aggregate. The match succeeds if all Parameters match.

    N.B.: To avoid unnecessary redundant recursive comparisons of Aggregate parameter values if the {@link Selector#Value(boolean) Value} criteria is included in the selection the {@link Selector#Parameter_Values_Match(Parameter, Parameter) data comparison} is only done for non-Aggregate Parameters.

    @param parameter The Parameter to be compared to this Parameter. @param criteria The Selector providing the comparison criteria. @return true if there is a criteria match; false otherwise. @see Selector @see Selection */ public boolean Match_Depth ( Parameter parameter, Selector criteria ) { boolean matches, match_data = criteria.Value (); if (match_data) // Defer Data_Match to non-Array values. criteria.Value (false); boolean end[] = {false}; matches = match_depth (parameter, criteria, match_data, end); if (match_data) criteria.Value (true); return matches; } private boolean match_depth ( Parameter parameter, Selector criteria, boolean match_data, boolean[] end ) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (">>> Parameter.match_depth:\n" +" this - " + this.Name () + '\n' +" parameter - " + parameter.Name () + '\n' +" " + criteria + '\n' +" match_data - " + match_data); if (parameter == null || ! criteria.Parameters_Match (this, parameter)) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Parameter.match_depth: false"); return false; } boolean matches = true; if ( this.Is_Aggregate () && parameter.Is_Aggregate ()) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" Comparing Aggregate Lists ..."); Iterator these_parameters, those_parameters; if (criteria.Specific ()) { // Compare the entire lists. if ( this.List_Size () != parameter.List_Size ()) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" this.List_Size " + this.List_Size () + " != parameter.List_Size " + parameter.List_Size () + '\n' +"<<< Parameter.match_depth: false"); return false; } try { Vector this_list = this.List (), that_list = parameter.List (); if (this_list == null && that_list == null) // Empty lists. { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" Empty lists.\n" +"<<< Parameter.match_depth: true"); return true; } these_parameters = this_list.iterator (); those_parameters = that_list.iterator (); } catch (PVL_Exception exception) { // Shouldn't happen since they're both valid aggregates. if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" PVL_Exception!\n" +"<<< Parameter.match_depth: false"); return false; } } else { /* END_XXX parameters will end the lists. END_PVL parameters will be specially noticed and the end flag will be set so that no further comparisons will be done up the recursion line. */ these_parameters = this.iterator (); those_parameters = parameter.iterator (); } while (these_parameters.hasNext () && those_parameters.hasNext ()) { if (! ((Parameter)these_parameters.next ()).match_depth ((Parameter)those_parameters.next (), criteria, match_data, end)) { matches = false; break; } if (end[0]) // END_PVL in sub-parameter. break; } if (! end[0] && ! criteria.Specific ()) { if (these_parameters.hasNext () || those_parameters.hasNext ()) // The lists are not the same size. { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" The lists are not the same size"); matches = false; } // Test for END_PVL in either list. try { these_parameters.next (); those_parameters.next (); } catch (NoSuchElementException exception) { if (exception.getMessage ().startsWith (Parameter.Classification_Name (Parameter.END_PVL) + "\n")) // END_PVL in list. end[0] = true; } } } else if (match_data) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" Comparing Assignment Values ..."); if (this.Data () == null) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" this Data is null\n" +" parameter Data is " + ((parameter.Data () != null) ? "not " : "") + "null\n" +"<<< Parameter.match_depth: " + (parameter.Data () == null)); return (parameter.Data () == null); } if (parameter.Data () == null) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" this Data is not null\n" +" parameter Data is null\n" +"<<< Parameter.match_depth: false"); return false; } try {matches = this.Value ().Match_Depth (parameter.Value (), criteria);} catch (PVL_Exception exception) { // Shouldn't happen since they're both valid assignments. matches = false; } } if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Parameter.match_depth: " + matches); return matches; } /** Tests if an Object is equal to this Parameter.

    An Object is equal to this Parameter when it is also a Parameter object and all of its contents are equal to this Parameter's contents. A Selection object with PARAMETER_MATCH criteria is used with the Match_Depth method.

    @param object The Object to be compared to this Parameter. @return true if the object equals this Parameter, false otherwise. @see #Match_Depth(Parameter, Selector) @see Selection @see Selector#PARAMETER_MATCH */ public boolean equals ( Object object ) { if (! Is_Parameter (object)) return false; return Match_Depth ((Parameter)object, new Selection (Selector.PARAMETER_MATCH)); } /** Tests if a Parameter is equivalent to this Parameter, ignoring certain cases where field values are not equal.

    Unlike equals, for a Parameter to be equivalent to this Parameter the contents need only be logically equivalent, rather than containing the identical contents. For example, shortening the parameter list of an Aggregate by setting to END_AGGREGATE the classification of the first entry that is different from the entries of another aggregate will result in the two aggregates being logically equivalent. A Selection object with PARAMETER_MATCH but Specific (false) criteria is used with the Match_Depth method.

    @param parameter The Parameter to be compared to this Parameter. @return true if the Parameter is logically equivalent to this Parameter, false otherwise. @see #Match_Depth(Parameter, Selector) @see Selection @see Selector#PARAMETER_MATCH @see Selection#Specific(boolean) */ public boolean equalsIgnoreCase ( Parameter parameter ) { return Match_Depth (parameter, new Selection (Selector.PARAMETER_MATCH).Specific (false)); } /*------------------------------------------------------------------------------ Write: */ /** Writes the Parameter in PVL syntax using a PVL Lister.

    @param lister The Lister to write the Parameter. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the Lister's Write method. @see Lister#Write(Parameter) */ public int Write ( Lister lister ) throws PVL_Exception, IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Parameter.Write"); int total = lister.Write (this); Warning (lister.Warning ()); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Parameter Write: total written = " + total); return total; } /** Writes the Parameter in PVL syntax to an OutputStream.

    A Lister is created with the output stream as its list destination using specified indent level and syntax strictness.

    @param output The OutputStream to receive what is written. If output is null System.out is used. @param level The indent level at which to start the listing. If the level is negative, indenting is disabled. @param strict Use strict PVL syntax if true; otherwise use an easier to read format. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the Lister's Write method. @see Lister#Write(Parameter) */ public int Write ( OutputStream output, int level, boolean strict ) throws PVL_Exception, IOException { return Write (new Lister (output) .Indent_Level (level) .Indent_Arrays ((level < 0) ? false : true) .Strict (strict)); } /** Writes the Parameter in PVL syntax to System.out starting at level 0, not strict.

    @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write () throws PVL_Exception, IOException {return Write (System.out, 0, false);} /** Writes the Parameter in PVL syntax to System.out with or without indenting, not strict.

    @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( boolean indent ) throws PVL_Exception, IOException {return Write (System.out, (indent ? 0 : -1), false);} /** Writes the Parameter in PVL syntax to output System.out with indenting and strict PVL enabled or disabled.

    @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @param strict Use strict PVL syntax if true; otherwise use an easier to read format. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( boolean indent, boolean strict ) throws PVL_Exception, IOException {return Write (System.out, (indent ? 0 : -1), strict);} /** Writes the Parameter in PVL syntax to the output starting at level 0, not strict.

    @param output The OutputStream to receive what is written. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( OutputStream output ) throws PVL_Exception, IOException {return Write (output, 0, false);} /** Writes the Parameter in PVL syntax to the output with or without indenting, not strict.

    @param output The OutputStream to receive what is written. @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( OutputStream output, boolean indent ) throws PVL_Exception, IOException {return Write (output, (indent ? 0 : -1), false);} /** Writes the Parameter in PVL syntax to the output with indenting and strict PVL enabled or disabled.

    @param output The OutputStream to receive what is written. @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @param strict Use strict PVL syntax if true; otherwise use an easier to read format. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( OutputStream output, boolean indent, boolean strict ) throws PVL_Exception, IOException {return Write (output, (indent ? 0 : -1), strict);} /*------------------------------------------------------------------------------ Iterators: */ /** Provides an Iterator for the aggregate list positioned so that the next element returned will be at the specified index of the list.

    This Iterator does not descend into the lists of aggregate Parameters in the current list. The {@link DefaultMutableTreeNode DefaultMutableTreeNode} base class provdes various Enumeration objects that will traverse the hierarchy in different orders.

    In addition to implementing the Iterator interface, the implementation for a Parameter treats END Parameters as the end of the list. The hasNext method will return false when the next Parameter in the list has an END classification. To distinguish this case from the usual case when the end of the list has been reached, the next method will include this information when it throws NoSuchElementException at the end of the list. If the end of the list is as result of an END Parameter, then the getMessage method for the exception will contain the {@link #Classification_Name() Classification_Name} as the first line (String terminated by a new-line ('\n') character) of the message String. Otherwise the message will only contain the ID of the Parameter class module (in all cases a non-null message String will be provided). For example:

    Iterator list = aggregate.iterator ();
    while (list.hasNext ())
        {
        Parameter parameter = (Parameter)list.next ();
        // Do something with the parameter.
        }
    try {list.next ();}
    catch (NoSuchElementException exception)
        {
        if (exception.getMessage ().startsWith
            (Parameter.Classification_Name (Parameter.END)))
            {
            if (exception.getMessage ().startsWith
                (Parameter.Classification_Name (Parameter.END_PVL) + '\n'))
                // The END_PVL Parameter was encountered.
            else
                // An END_AGGREGATE was encountered.
            }
        }
    

    A starting index beyond an END Parameter may be specified to skip over it.

    For non-aggregate Parameters an "empty" Iterator will be provided. This will act the same as an Iterator on an empty list.

    @param index The starting list element. @return An Iterator object. @see Iterator @see #Classification_Name() @see DefaultMutableTreeNode */ public Iterator iterator ( int index ) { if (Is_Aggregate ()) return new Parameter_Iterator (index); return EMPTY_LIST_ITERATOR; } /** Provides an Iterator starting at the first element of the list.

    @return An Iterator object. @see #iterator(int) */ public Iterator iterator () {return iterator (0);} /** Provides a ListIterator for the aggregate list positioned so that the next element returned will be at the specified index of the list.

    The functionality of the Parameter Iterator is provided (which it extends) with the ListIterator interface implemented.

    As with the Parameter Iterator, END Parameters are treated as the end of the list. During reverse iteration (hasPrevious and previous) END Parameters mark an artificial beginning of the list, when the start index is positioned beyond one. In this case the same special message is provided with the NoSuchElementException object as for the next method.

    Note: When the Parameter is an aggregate, but has no list, an empty one is provided so the add method will be able to insert new Parameters into the list.

    @param index The starting list element. @return A ListIterator object. @see #iterator() @see ListIterator */ public ListIterator listIterator ( int index ) { if (Is_Aggregate ()) return new Parameter_ListIterator (index); return EMPTY_LIST_ITERATOR; } /** Provides a ListIterator starting at the first element of the list.

    @return A ListIterator object. @see #listIterator(int) */ public ListIterator listIterator () {return listIterator (0);} /*............................................................................. */ private class Parameter_Iterator implements Iterator { ListIterator list_iterator; Parameter last = null; String next_end = null, previous_end = null; private Parameter_Iterator () {this (0);} private Parameter_Iterator ( int index ) { if (children == null) children = new Vector (); // May need to add to this aggregate. list_iterator = children.listIterator (index); } public boolean hasNext () { if (list_iterator.hasNext () && next_end == null) { // Don't iterate over an END Parameter. Parameter parameter = (Parameter)children.get (list_iterator.nextIndex ()); if (! parameter.Is_End ()) return true; next_end = parameter.Classification_Name (); } return false; } public Object next () { if (hasNext ()) { previous_end = null; return (last = (Parameter)list_iterator.next ()); } if (next_end != null) /* Start the exception message with the END_XXX classification name. >>> WARNING <<< This is the only way to tell that the NoSuchElementException was thrown due to an END_XXX Parameter and which specific classification. */ throw new NoSuchElementException (next_end + "\n" + ID); throw new NoSuchElementException (ID); } public void remove () { if (last != null) { list_iterator.remove (); last.parent = null; last = null; return; } throw new IllegalStateException (ID); } } // End of Parameter_Iterator class private class Parameter_ListIterator extends Parameter_Iterator implements ListIterator { private Parameter_ListIterator ( int index ) {super (index);} public boolean hasPrevious () { if (list_iterator.hasPrevious ()) { // Don't iterate over an END Parameter. Parameter parameter = (Parameter)children.get (list_iterator.previousIndex ()); if (! parameter.Is_End ()) return true; previous_end = parameter.Classification_Name (); } return false; } public Object previous () { if (hasPrevious ()) { next_end = null; return (last = (Parameter)list_iterator.previous ()); } if (previous_end != null) throw new NoSuchElementException (previous_end + "\n" + ID); throw new NoSuchElementException (ID); } public int nextIndex () {return list_iterator.nextIndex ();} public int previousIndex () {return list_iterator.previousIndex ();} public void set ( Object object ) { if (! Is_Parameter (object)) throw new ClassCastException (ID + "\nThe Object to set is not a Parameter."); if (last != null) { list_iterator.set (object); ((Parameter)object).parent = last.parent; last.parent = null; last = (Parameter)object; return; } throw new IllegalStateException (ID); } public void add ( Object object ) { if (! Is_Parameter (object)) throw new ClassCastException (ID + "\nThe Object to set is not a Parameter."); list_iterator.add (object); ((Parameter)object).parent = Parameter.this; last = null; } } // End of Parameter_ListIterator class static final ListIterator EMPTY_LIST_ITERATOR = new ListIterator () { public boolean hasNext () {return false;} public int nextIndex () {return 0;} public Object next () {throw new NoSuchElementException (ID);} public boolean hasPrevious () {return false;} public int previousIndex () {return -1;} public Object previous () {throw new NoSuchElementException (ID);} public void remove () {throw new UnsupportedOperationException ();} public void set (Object object) {throw new UnsupportedOperationException ();} public void add (Object object) {throw new UnsupportedOperationException ();} }; // End of Null_Iterator class /*------------------------------------------------------------------------------ Find: */ /** Searches for a Parameter that matches a test Parameter according the Selector criteria, optionally after some specific Parameter object has been located first.

    A search is made of the Parameters contained in this aggregate, and, recursively, any aggregates it contains. The search is depth-wise: each aggregate Parameter is searched when it is encountered.

    The search must first find the last_parameter (having the same object reference) before proceeding with the search for the test_parameter beginning with the Parameter following the last_parameter. If the last_parameter is null, then the search for the test_parameter begins with the first Parameter contained in this aggregate.

    The search compares each Parameter encountered against the test_parameter using the Selector criteria's Parameters_Match method. The first Parameter that matches the test_parameter is returned. If the last_parameter or a match for the test_parameter is not found, null is returned. If the last_parameter could not be found a PVL_Exception.NO_LAST_LOCATION warning status is registered with this Parameter. If this Parameter in which to Find is not an aggregate, null will be returned and a PVL_Exception.ILLEGAL_SYNTAX warning status will be set.

    The test_parameter may be any parameter, pre-existing or created ad hoc as a template for the Selector criteria. A Selector offers numerous criteria that may be conditionally applied when comparing the test_parameter with prospective mathing parameters. Note: The PIRL.PVL package provides a Selection class that implements the Selector interface.

    In addition, when the Selector criteria enables {@link Selector#Name(boolean) Name} matching the search engine will provide pathname tracking. This allows the name of the test_parameter to be a full (absolute) or partial (relative) pathname to a Parameter, of the form:

    [ / ] Name [ /Name [...] ]

    The name of the test_parameter may be a simple name; i.e. it contains no {@link #Path_Delimiter() Path_Delimiter} characters. In this case a Parameter will be selected when its name matches the test_parameter name entirely. A pathname beginning with a Path_Delimiter character is "absolute" in that a Parameter with a Name that matches the first segment of the pathname must be found in this Parameter's aggregate list - the root of the Find - before the next segment of the pathname will qualify for a match of a Parameter Name in the aggregate list of the first Parameter matched; etc. Note: The first name of an absolute pathname is not the name of this Parameter, it is the name of the Parameter to find in the root aggregate list. All Parameters in the pathname sequence must, of course, be aggregates except the last Parameter (which may be of any classification). This is the same as file pathnames in Unix. A pathname that does not begin with a Path_Delimiter is "relative" in that the first Name is searched for like a simple name and then the remainder of the path is searched for relative to the location of the first Parameter found. The remainder of the pathname after the first name is treated like an absolute pathname.

    N.B.: The current {@link #Warning() warning status} is {@link #Reset_Warning() reset} before the search begins.

    @param test_parameter A Parameter object to use for testing Parameters in this aggregate for a match. If null, or its Name is null, null is immediately returned. @param criteria A Selector object providing the methods to determine if Parameters match. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. If the Parameter in which to Find is not an aggregate, null will be returned and a PVL_Exception.ILLEGAL_SYNTAX warning status will be set. If the last_parameter could not be found a PVL_Exception.NO_LAST_LOCATION warning status is registered with this Parameter. @see Selector @see Selection#Parameters_Match(Parameter, Parameter) @see #Path_Name() */ public Parameter Find ( Parameter test_parameter, Selector criteria, Parameter last_parameter ) { if (! find_check (test_parameter)) return null; // Non-null when searching for last_parameter. Parameter[] Last_Parameter = {last_parameter}; /* User specified END parameter flag. Used the same way as in the Write method. */ int[] end = {0}; if ((DEBUG & DEBUG_FIND) != 0) { System.out.println ( ">>> Parameter Find:\n" + " criteria = " + Integer.toString (criteria.Criteria (), 2) + "\n" + " Name = " + criteria.Name () + "\n" + " Classification = " + criteria.Classification () + "\n" + " Comments = " + criteria.Comments () + "\n" + " Value = " + criteria.Value () + "\n" + " Data = " + criteria.Data () + "\n" + " Type = " + criteria.Type () + "\n" + " Base = " + criteria.Base () + "\n" + " Units = " + criteria.Units () + "\n" + " Specific = " + criteria.Specific () + "\n" + " Pattern_Match = " + criteria.Pattern_Match () + "\n" + " AND = " + criteria.And () ); if (last_parameter != null) System.out.println (" last_parameter is \"" + last_parameter.Path_Name () + "\""); } test_parameter = find (test_parameter, criteria, Last_Parameter, end); if (test_parameter == null && Last_Parameter[0] != null && Warning () == null) Warning ( PVL_Exception.NO_LAST_LOCATION, "Can't Find the last Parameter named \"" + last_parameter.Name () + "\"." ); return test_parameter; } /** Finds the Parameter with the specified name, which may be a simple name or a pathname.

    The name string matching is case sensitive.

    @param name The name or pathname String to search for. If null, the empty String will be used. @return The Parameter object that was found, or null if not found. @see #Find(String, boolean, Parameter) */ public Parameter Find ( String name ) {return Find (name, true, null);} /** Finds the Parameter with the specified name, which may be a simple name or a pathname, beginning after the specified Parameter object within the aggregate.

    The name string matching is case sensitive.

    @param name The name or pathname String to search for. If null, the empty String will be used. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. @see #Find(String, boolean, Parameter) */ public Parameter Find ( String name, Parameter last_parameter ) {return Find (name, true, last_parameter);} /** Finds the Parameter with the specified name, which may be a simple name or a pathname.

    @param name The name or pathname String to search for. If null, the empty String will be used. @param case_sensitive true for the case sensitive name matching; false for case insensitive name matching. @return The Parameter object that was found, or null if not found. @see #Find(String, boolean, Parameter) */ public Parameter Find ( String name, boolean case_sensitive ) {return Find (name, case_sensitive, null);} /** Finds the Parameter with the specified name, which may be a simple name or a pathname, beginning after the specified Parameter object within the aggregate.

    @param name The name or pathname String to search for. If null, the empty String will be used. @param case_sensitive true for the case sensitive name matching; false for case insensitive name matching. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. @see #Find(Parameter, Selector, Parameter) @see Selection#Name(boolean) @see Selection#Specific(boolean) */ public Parameter Find ( String name, boolean case_sensitive, Parameter last_parameter ) { return Find ( new Parameter (name), new Selection ().Name (true).Specific (case_sensitive), last_parameter ); } /** Finds the Parameter having the specified classification.

    The classification matching is exact. For a general classification match - e.g. a GROUP will match with an OBJECT - use:

    Find (new Parameter ().Classification (classification),
          new Selection ().Classification (true).Specific (false));
    

    @param classification The classification code to search for. @return The Parameter object that was found, or null if not found. @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( int classification ) {return Find (classification, null);} /** Finds the Parameter having the specified classification, beginning after the specified Parameter object within the aggregate.

    @param classification The classification code to search for. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. @see #Find(int) @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( int classification, Parameter last_parameter ) { return Find ( new Parameter ().Classification (classification), new Selection ().Classification (true), last_parameter ); } /** Finds the Parameter having the specified pathname and classification.

    Parameter pathname matching is specific (case sensitive) but classification matching is non-specific (any Aggregate type).

    @param name The name or pathname String to search for. If null, the empty String will be used. @param classification The classification code to search for. @return The Parameter object that was found, or null if not found. @see #Find(String, int, Parameter) */ public Parameter Find ( String name, int classification ) {return Find (name, classification, null);} /** Finds the Parameter having the specified pathname and classification, beginning after the specified Parameter object within the aggregate.

    Parameter pathname matching is specific (case sensitive) but classification matching is non-specific (any Aggregate type).

    @param name The name or pathname String to search for. If null, the empty String will be used. @param classification The classification code to search for. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. @see #Find(String) @see #Find(int) @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( String name, int classification, Parameter last_parameter ) { Selector criteria = new Selection () .Name (true) .Specific (true); Parameter test_parameter = new Parameter (name).Classification (classification), selected_parameter; while ((selected_parameter = Find (test_parameter, criteria, last_parameter)) != null) { if (criteria .Specific (false) .Classifications_Match (selected_parameter, test_parameter)) break; last_parameter = selected_parameter; criteria.Specific (false); } return selected_parameter; } /** Finds the Parameter that completely matches a test Parameter.

    A complete match requires an exact match of the name (which may be a pathname), classification, comments, and Value when the Parameter is an assignment. The Value match requires that the data and its type (as well as the numeric base for integers), and any units String all be exactly equal. Note: Parameter contents, not Parameter object references, are compared.

    @param test_parameter The Parameter to use when testing for a match. @return The Parameter object that was found, or null if not found. @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( Parameter test_parameter ) { return Find ( test_parameter, new Selection ().Criteria (Selector.PARAMETER_MATCH), null ); } /** Finds the Parameter that completely matches a test Parameter, beginning after the specified Parameter object within the aggregate.

    @param test_parameter The Parameter to use when testing for a match. @param last_parameter A Parameter object to be found within the aggregate before search comparisons begin. If null, begin the search at the first element of the aggregate list. @return The Parameter object that was found, or null if not found. @see #Find(Parameter) @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( Parameter test_parameter, Parameter last_parameter ) { return Find ( test_parameter, new Selection ().Criteria (Selector.PARAMETER_MATCH), last_parameter ); } /** Finds the Parameter that matches a test Parameter according the Selector criteria.

    @param test_parameter The Parameter to use when testing for a match. @param criteria A Selector object providing the methods to determine if Parameters match. @return The Parameter object that was found, or null if not found. @see #Find(Parameter, Selector, Parameter) */ public Parameter Find ( Parameter test_parameter, Selector criteria ) {return Find (test_parameter, criteria, null);} private boolean find_check ( Parameter test_parameter ) { // So last parameter not found will be noticed. Reset_Warning (); if (! Is_Begin_Aggregate ()) { Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't Find in the " + Classification_Name () + " Parameter named \"" + Name () + "\"." ); return false; } // Guarantee that the test_parameter passed in will not be null. if (test_parameter == null || test_parameter.Name () == null || children == null) return false; return true; } private Parameter find ( Parameter Test_Parameter, Selector Criteria, Parameter[] Last_Parameter, int[] end ) { /* An array is used for Last_Parameter to provide a pass-back value when the Last_Parameter[0] has been located. */ if ((DEBUG & DEBUG_FIND) != 0) System.out.println (">>> Parameter find: In \"" + Path_Name () + "\""); Parameter parameter, found_parameter = null; String selection_name = Test_Parameter.Name (), path = null, name = null; boolean absolute_pathname = false; if (Criteria.Name ()) { /* Pathname tracking. Find a Parameter with a name on the Test_Parameter's pathname. The name of the Test_Parameter is a pathname of the form: [/]Name[/Name[...]] The name of the Test_Parameter may be a simple name; i.e. it contains no _Path_Delimiter_ characters. A Parameter will be selected when its name matches the Test_Parameter name. A pathname beginning with a _Path_Delimiter_ is "absolute" in that a Parameter with the first Name must be found in the Parameter list of the root (top) Parameter before the next Name will qualify for a match in a Parameter aggregate in this list; etc. All Parameters in the pathname sequence must be aggregates except the last Parameter (which may be of any classification). This is the same as file pathnames in Unix. A pathname that does not begin with a _Path_Delimiter_ is "relative" in that the first Name is searched for like a simple name and then the remainder of the path is searched for relative to the location of the first Parameter found. The remainder of the path after the first name is an absolute pathname. Note: The Test_Parameter Name is only null when just looking for the Last_Parameter while pathname tracking and this aggregate is off the potential path to the name. */ if ((name = selection_name) != null) { if (name.length () > 0 && name.charAt (0) == _Path_Delimiter_) { // Absolute pathname. name = name.substring (1); // Get the relative pathname, absolute_pathname = true; // but root it here. } /* Separate the name to find in the Parameter list of this aggregate from the remainder of the path, if any, to be passed on to any aggregate Parameters encountered in the aggregate list. The name is used in comparisons with the Name of each Parameter in the current aggregate list. The path, if any, is always an absolute pathname; i.e. it begins with the _Path_Delimiter_ that separated it from the leading name. If there is no path remaining then path will be null. */ int index = name.indexOf (_Path_Delimiter_); if (index >= 0) { // There's another delimiter in the name. path = name.substring (index); // The remainder of the path. name = name.substring (0, index); // The first name. } } // Set the Test_Parameter to the local name. Test_Parameter.set_name (name); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ( "Parameter find:\n" + " selection_name = " + selection_name + "\n" + " name = " + name + "\n" + " path = " + path + "\n" + " absolute_pathname = " + absolute_pathname ); } else // For restoring the name after searching aggregates. name = selection_name; /* Search the Parameters in this aggregate. Note: The algorithm of this function searches depth-wise; i.e. each aggregate encountered in the Parameter list is searched (conditionally) when it is encountered. */ ListIterator parameters = children.listIterator (); while (parameters.hasNext ()) { if (end[0] < 0) break; // Abort due to END_PVL parameter = (Parameter)parameters.next (); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("--- parameter \"" + parameter.Path_Name () + "\""); if (Last_Parameter[0] == null) { /* Compare the Parameter with the Test_Parameter using the Criteria. Note that the Test_Parameter comparison only occurs after the Last_Parameter has been found (or isn't being used). Note that the Test_Parameter is that_parameter for the Criteria Selection. This is important when pattern matching is used, because the regex pattern must be in that_parameter. */ if (Criteria.Parameters_Match (parameter, Test_Parameter)) { if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("Potential match"); found_parameter = parameter; if ((Criteria.Parameters_Match () & Selector.NAME) != 0 && path != null) { /* A Name match was selected. But the name has an additional path component to match. So this match is insufficient; the entire path need to be matched. */ if ((DEBUG & DEBUG_FIND) != 0) System.out.println (" More path: " + path); found_parameter = null; } else { if ((DEBUG & DEBUG_FIND) != 0) System.out.println (" MATCH"); break; // Found. } } } else { // Just searching for the Last_Parameter. if (parameter == Last_Parameter[0]) Last_Parameter[0] = null; // Found it. if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("Last_Parameter " + ((Last_Parameter[0] == null) ? "======= found" : "not found")); } if (parameter.Is_End ()) { /* User specified end of the Parameter list. Note that matching against this END Parameter is allowed, but pathname tracking beyond this point is not. */ if (parameter.Is_End_PVL ()) /* User specified end of all Parameters. Hit the abort button. This Parameter effectively marks the end of all Parameters regardless of what may exist at higer levels. */ end[0] = -1; else end[0] = 1; break; } Aggregate_Search: if (parameter.Is_Begin_Aggregate () && parameter.List_Size () != 0) { if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("Aggregate search ---"); /* Aggregate search. The_Parameter is an aggregate and a decision needs to be made on whether to extend the search into it's Parameter list and, if so, how to conduct the search there. There are two possible cases for searching into an aggregate Parameter list: a) pathname tracking mode and b) all others. The inclusion of pathname tracking when searching for a Parameter name complicates the logic, which would ordinarily be to always extend the search into aggregates. Only pathname tracking must be evaluated specially. */ if (Criteria.Name ()) { /* Pathname tracking. If the search in this aggregate fails and the Test_Parameter specified a relative pathname, then the search will be retried with the full Test_Parameter Name. In this case the state of the Last_Parameter search will need to be restored to its state before searching in the aggregate. */ String pathname = path; Parameter last_parameter = Last_Parameter[0]; if (name != null) { /* We've been passed a name to find so we're on the path. Is the aggregate Parameter also on the path? */ if (! Criteria.Names_Match (parameter, Test_Parameter)) // The aggregate is not on the path. pathname = null; } if ((DEBUG & DEBUG_FIND) != 0) System.out.println (" Tracking pathname \"" + pathname + "\"\n" + " Looking for Last_Parameter? " + (last_parameter != null)); if (Last_Parameter[0] != null || pathname != null) { /* Search this aggregate for the absolute pathname. This search is always done when still looking for the Last_Parameter or when the aggregate is still on the path. This search doesn't happen if the Last_Parameter has already been found and this aggregate is off the path; i.e if we're in the Test_Parameter criteria phase and there's no pathname to find, then this search won't happen. In this case the aggregate search will be done below instead. */ Test_Parameter.set_name (pathname); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("\n Searching for pathname \"" + pathname + "\" in \"" + parameter.Path_Name () + "\""); found_parameter = parameter.find ( Test_Parameter, Criteria, Last_Parameter, end ); if (found_parameter != null) break; // Found a selection. Test_Parameter.set_name (name); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("\n Resuming search for selection_name \"" + selection_name + "\" in \"" + Path_Name () + "\""); if (Last_Parameter[0] == null) { // The Last_Parameter has been found. if ((DEBUG & DEBUG_FIND) != 0) System.out.println (" Last_Parameter was found."); if (name == null) { // We're not on the path here. if (Criteria.And () || Criteria.Parameter_Criteria () == Selector.NAME) /* A pathname match is required but it can't be found in the current Parameter list. */ break; /* There's no selection_name so there's no reason to retry the search of the aggregate. */ break Aggregate_Search; } if (absolute_pathname) /* The search will only be repeated when the selection name is a relative path and the Last_Parameter has been found. */ break Aggregate_Search; else /* Restore the Last_Parameter. Note: For a relative pathname the search will be retried using the full selection_name, so the state of the Last_Parameter from before the aggregate search needs to remain in effect. */ Last_Parameter[0] = last_parameter; } else /* It's no use retrying in any case since the Last_Parameter couldn't be found. */ break Aggregate_Search; } // End of pathname search in the aggregate. } // End of pathname tracking. if (! absolute_pathname) { // Search the aggregate using the full selection_name. Test_Parameter.set_name (selection_name); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("\n Searching for selection_name \"" + selection_name + "\" in \"" + parameter.Path_Name () + "\""); found_parameter = parameter.find ( Test_Parameter, Criteria, Last_Parameter, end ); if (found_parameter != null) break; // Found a criteria. Test_Parameter.set_name (name); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("\n Resuming search for selection_name \"" + selection_name + "\" in \"" + Path_Name () + "\""); } } // End of aggregate search. } // End of Parameter list. // Restore the full selection name. Test_Parameter.set_name (selection_name); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("<<< Parameter find: found_parameter is " + (found_parameter != null)); return found_parameter; } /*============================================================================== Methods that override base class methods: */ /* Errors are reported as a Warning. It would be better if there were a way to tell the caller directly about problems. Unfortunately, the flawed design of the base class methods does not provide any means to safely report back to the caller, not even a function return value. */ /** Overrides the base class method to ensure Parameter integrity.

    @param object Since the userObject of a Parameter is it's name, only String objects are allowed for the argument. Anything else is ignored and a PVL_Exception.BAD_ARGUMENT warning status is set. @see DefaultMutableTreeNode#setUserObject(Object) */ public void setUserObject ( Object object ) { if (object instanceof String) userObject = object; else Warning ( PVL_Exception.BAD_ARGUMENT, "Can't setUserObject of " + Classification_Name () + " Parameter named \"" + Name () + "\"\n" + " to " + object.getClass ().getName () + " object.\n" + " Use the Name (String) method." ); } /** Overrides the base class method to ensure Parameter integrity.

    @param allows Since the ability of a Parameter to have children is totally determined by its classification (only aggregates may have a children list), this argument is ignored and a PVL_Exception.ILLEGAL_SYNTAX warning status is set. @see DefaultMutableTreeNode#setAllowsChildren(boolean) */ public void setAllowsChildren ( boolean allows ) { Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't setAllowsChildren of " + Classification_Name () + " Parameter named \"" + Name () + "\"\n" + " This is determined by the Parameter's Classification." ); } /** Invokes the Parameter's Add method.

    @param parameter A MutableTreeNode that must be a Parameter object. @throws IllegalArgumentException If the parameter argument is not a Parameter object, or the Add method threw a PVL_Exception (in which case it's message is used for the exception message). @see #Add(Parameter) @see DefaultMutableTreeNode#add(MutableTreeNode) */ public void add ( MutableTreeNode parameter ) throws IllegalArgumentException { if (! Is_Parameter (parameter)) throw new IllegalArgumentException ( ID + "\n" + "The argument to add is not a Parameter." ); try {Add ((Parameter)parameter);} catch (PVL_Exception exception) {throw new IllegalArgumentException (exception.getMessage ());} } /** Invokes the Parameter's Insert method.

    @param parameter A MutableTreeNode that must be a Parameter object. @param index The index in the aggregate list where the Parameter is to be inserted. @throws IllegalArgumentException If the parameter argument is not a Parameter object, or the Insert method threw a PVL_Exception (in which case it's message is used for the exception message). @see #Insert(Parameter, int) @see DefaultMutableTreeNode#insert(MutableTreeNode, int) */ public void insert ( MutableTreeNode parameter, int index ) throws IllegalArgumentException { if (! Is_Parameter (parameter)) throw new IllegalArgumentException ( ID + "\n" + "The argument to insert is not a Parameter." ); try {Insert ((Parameter)parameter, index);} catch (PVL_Exception exception) {throw new IllegalArgumentException (exception.getMessage ());} } /** Invokes the Parameter's Remove method.

    @param index The index of the element in the aggregate list to remove. @see #Remove(int) @see DefaultMutableTreeNode#remove(int) */ public void remove ( int index ) {Remove (index);} /** Invokes the Parameter's Remove method.

    @param parameter A MutableTreeNode that must be a Parameter object. @throws IllegalArgumentException If the parameter argument is not a Parameter object. @see #Remove(Parameter) @see DefaultMutableTreeNode#remove(MutableTreeNode) */ public void remove ( MutableTreeNode parameter ) throws IllegalArgumentException { if (! Is_Parameter (parameter)) throw new IllegalArgumentException ( ID + "\n" + "The argument to remove is not a Parameter." ); Remove ((Parameter)parameter); } /*------------------------------------------------------------------------------ */ private void Warning ( String description, String explanation ) { Warning ( new PVL_Exception ( ID, description, explanation ) ); } private void Warning ( PVL_Exception warning ) { if (warning != null) { _Last_Warning_ = warning; if (_First_Warning_ == null) _First_Warning_ = _Last_Warning_; } } } // End of class pirl-2.3.8/PIRL/PVL/Value.java0000644000175000017500000031405511742734277015520 0ustar mathieumathieu/* Value PIRL CVS ID: Value.java,v 1.30 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; // The base class import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.MutableTreeNode; import java.lang.Math; import java.util.List; import java.util.Vector; import java.util.Arrays; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; import java.io.OutputStream; import java.io.StringWriter; import java.io.IOException; /** A Value is a general-purpose value entity containing arbitrary data.

    A Value provides the data value for Parameter objects. It is based on the Parameter Value Language syntax:

    [ ( | { ] Datum [ < Units > ] [ , Datum [...] ] [ ) | } [ < Units > ] ]

    The type of Datum that a Value contains is indicated by its Type code. There are three basic types:

    NUMERIC
    A number with two specific types:
    INTEGER (Long) with a variable radix Base
    REAL (Double)
    STRING
    A String of characters with four specific types:
    IDENTIFIER - Unquoted
    SYMBOL - Single-quoted (')
    TEXT - Double-quoted (")
    DATE_TIME - Contains dash (-) or colon (:)
    ARRAY
    A Vector of zero or more Datum, with two specific types:
    SET - Enclosed by curly braces ('{' and '}') or no enclosing characters
    SEQUENCE - Enclosed by parentheses ('(' and ')')

    There is also an UNKNOWN Type for a Value with no Datum. Note that any Datum may be an ARRAY Type, so Arrays of Arrays are supported. Arrays may be of mixed Types and non-rectangular sizes.

    Any Datum, including an Array, may have an optional Units String that provides an arbitrary description for the Datum.

    @see Parameter @see Parser @author Bradford Castalia, UA/PIRL @version 1.30 */ public class Value extends DefaultMutableTreeNode implements Cloneable { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Value (1.30 2012/04/16 06:14:23)"; /* A DefaultMutableTreeNode contains a userObject that is displayed using its toString method in a JTree. For our purposes the userObject is the Value object itself. */ /* When a Value is single-valued (NUMERIC or STRING), _Datum_ is used to hold the object of the specific Type. However, when a Value is an ARRAY, then the children variable in the base class holds a Vector of Values. */ private Object _Datum_ = null; private int _Type_ = UNKNOWN; /* Value _Type_ codes: There are three basic Value Types: _Type_ _Datum_ object class --------- ------------------------------------------- NUMERIC: INTEGER Long REAL Double STRING: String IDENTIFIER - Unquoted SYMBOL - Single-quoted (') TEXT - Double-quoted (") DATE_TIME - Contains dash (-) or colon (:) ARRAY: null (children Vector contains Values) SET - Enclosed by curly braces ('{' and '}') or no enclosing characters SEQUENCE - Enclosed by parentheses ('(' and ')') Each element of an ARRAY children Vector is a Value (which may include another ARRAY children Vector). In addition there is the UNKNOWN Type for empty Values. Every basic Type has specific Types. Only in the case of the INTEGER and REAL NUMERIC Types does this distinction have any significance here. Note that Value _Type_ codes are expected to be distinct from Parameter _Classification_ codes. */ /** The lowest bit used by the Type code bit field. */ public static final int LOWEST_BIT = 0; /** The highest bit used by the Type code bit field. */ public static final int HIGHEST_BIT = LOWEST_BIT + 9; /** The classification of an empty Value. */ public static final int UNKNOWN = 0; /** Non-specific Type for a Value with a numeric datum.

    The specific Type will be either INTEGER or REAL.

    @see #INTEGER @see #REAL */ public static final int NUMERIC = 1 << LOWEST_BIT; /** Type for a Value with a Long datum. */ public static final int INTEGER = NUMERIC | (NUMERIC << 1); /** Type for a Value with a Double datum. */ public static final int REAL = NUMERIC | (NUMERIC << 2); /** Non-specific Type for a Value with a String datum.

    The specific Type will be either IDENTIFIER, SYMBOL, TEXT, or DATE_TIME.

    @see #IDENTIFIER @see #SYMBOL @see #TEXT @see #DATE_TIME */ public static final int STRING = 1 << (LOWEST_BIT + 3); /** Type for a Value with a String datum.

    It will be written unadorned (no quoting). */ public static final int IDENTIFIER = STRING | (STRING << 1); /** Type for a Value with a String datum.

    It will be written enclosed within {@link Parser#SYMBOL_DELIMITER Parser.SYMBOL_DELIMITER} characters. */ public static final int SYMBOL = STRING | (STRING << 2); /** Type for a Value with a String datum.

    It will be written enclosed within {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} characters. */ public static final int TEXT = STRING | (STRING << 3); /** Type for a Value with a String datum.

    It may represent a date or time (it contains one or more {@link Parser#DATE_TIME_DELIMITERS Parser.DATE_TIME_DELIMITERS}). */ public static final int DATE_TIME = SYMBOL | TEXT; /** Non-specific Type for a Value with a Vector datum.

    The specific Type will be either SET or SEQUENCE.

    @see #SET @see #SEQUENCE */ public static final int ARRAY = 1 << (LOWEST_BIT + 7); /** Type for a Value with a datum that is a Vector of Values.

    It will be written preceeded by a {@link Parser#SET_START_DELIMITER Parser.SET_START_DELIMITER} character and ending with a {@link Parser#SET_END_DELIMITER Parser.SET_END_DELIMITER} character. */ public static final int SET = ARRAY | (ARRAY << 1); /** Type for a Value with a datum that is a Vector of Values.

    It will be written preceeded by a {@link Parser#SEQUENCE_START_DELIMITER Parser.SEQUENCE_START_DELIMITER} character and ending with a {@link Parser#SEQUENCE_END_DELIMITER Parser.SEQUENCE_END_DELIMITER} character. */ public static final int SEQUENCE = ARRAY | (ARRAY << 2); private static int Default_Array_Type = SET; /** Selects only the Type code bit fields recognized by the Value class.

    Logical AND with another int variable will ensure that only Value Type bits remain. */ public static final int MASK = 0x3FF; /* INETEGER _Datum_ may be represented in any (valid) base. Non-decimal base integers are representated with the syntax: base#value# where "base" is the decimal representation of the number base for the value, and "value" is represented using the corresponding number base digits. Valid base values are in the range Character.MIN_RADIX to Character.MAX_RADIX. */ private int _Base_ = 10; // The units string is application dependent. private String _Units_ = null; // When throwing an exception is inappropriate. private PVL_Exception _First_Warning_ = null, _Last_Warning_ = null; private boolean _Use_First_Warning_ = true; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_DATA = 1 << 1, DEBUG_MODIFY = 1 << 2, DEBUG_TYPE = 1 << 3, DEBUG_WRITE = 1 << 4, DEBUG_MATCH = 1 << 5, DEBUG_FIND = 1 << 6, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates an empty Value, with Type UNKNOWN. */ public Value () {userObject = this;} /** Creates an IDENTIFIER Value from the primitive char value.

    @param value The data used to construct the Value. @see #Data(char) */ public Value (char value) {this (); Data (value);} /** Creates an INTEGER Value from the primitive byte value.

    @param value The data used to construct the Value. @see #Data(byte) */ public Value (byte value) {this (); Data (value);} /** Creates an INTEGER Value from the primitive short value.

    @param value The data used to construct the Value. @see #Data(short) */ public Value (short value) {this (); Data (value);} /** Creates an INTEGER Value from the primitive int value.

    @param value The data used to construct the Value. @see #Data(int) */ public Value (int value) {this (); Data (value);} /** Creates an INTEGER Value from the primitive long value.

    @param value The data used to construct the Value. @see #Data(long) */ public Value (long value) {this (); Data (value);} /** Creates a REAL Value from the primitive float value.

    @param value The data used to construct the Value. @see #Data(float) */ public Value (float value) {this (); Data (value);} /** Creates a REAL Value from the primitive double value.

    @param value The data used to construct the Value. @see #Data(double) */ public Value (double value) {this (); Data (value);} /** Creates a Value whose Type depends on the class of the value object.

    @param value The object used to construct the Value. @throws PVL_Exception From the Data method. @see #Data(Object) */ public Value (Object value) throws PVL_Exception {this (); Data (value);} /** A Parser is used to get a new Value.

    @param parser The Parser source for PVL syntax. @throws PVL_Exception When the Parser encounters an unrecoverable error. The Parser may also generate a Warning which will associated with the new Parameter. @see Parser */ public Value ( Parser parser ) throws PVL_Exception { this (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Value construction from a Parser"); parser.Reset_Warning (); // Start clean. // Graft the Value from the parser into this Value. graft (parser.Get_Value ()); _First_Warning_ = _Last_Warning_ = parser.Warning (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Value construction from Parser completed."); } /** A Value is constructed from a copy of another Value.

    The contents of the original Value are copied into the new Value. This includes its datum, radix base and units String. When the original Value is an Array, each datum is recursively copied and added to the new Value in the same order. The warning status from the original is not copied.

    @param value The original Value to be copied. @throws PVL_Exception

    BAD_ARGUMENT
    An inappropriate object was found as the element of an Array.
    @see #set_type(int) @see #Add(Value) */ public Value ( Value value ) throws PVL_Exception { this (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Value: Constructing from " + value); _Base_ = value._Base_; _Units_ = value._Units_; set_type (value._Type_); if (value.Is_Array ()) { ListIterator list = value.listIterator (); while (list.hasNext ()) { Object data = list.next (); if (! Is_Value (data)) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "While constructing a new Value\n" + " from the " + value.Type_Name () + " Value,\n" + " element " + list.previousIndex () + " is an object of class " + data.getClass ().getName () + "." ); /* Add a copy of the Value from the list. This will recursively replicate any ARRAY Values. */ if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("Value: Add " + (Value)data); Add (new Value ((Value)data)); } } else // All _Datum_ are final classes that can be assigned directly. _Datum_ = value._Datum_; // Don't copy the warning status. } /*============================================================================== Accessors */ /*------------------------------------------------------------------------------ Datum: */ /*.............................................................................. Get */ /** Gets a Long object from the Value.

    If the Value is Type INTEGER this is the unmodified datum, unless the datum is null in which case a Long with a 0 value is created. If the Value is Type REAL then a Long will be created from the Double datum by casting (which may result in loss of accuracy). If the Value is a STRING Type, then a Long will be constructed from the Value's long_Data method value. Note: The current radix base for the Value will be used when converting a String to a Long.

    @return A Long object from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is not a NUMERIC Type, or a STRING Type could not be converted to a long.
    @see #long_Data() */ public Long Long_Data () throws PVL_Exception { if (Is_Numeric ()) { if (_Datum_ == null) return new Long (0L); if (Is_Integer ()) return (Long)_Datum_; return new Long (((Double)_Datum_).longValue ()); } if (Is_String ()) return new Long (long_Data ()); throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a Long from a value of Type " + Type_Name () + "." ); } /** Gets a primitive long value from the Value.

    If the Value is Type INTEGER this is the long value contained in the Long datum, unless the datum is null in which case a value of 0L is returned. If the Value is Type REAL then a long value will be created from the Double datum by casting (which may result in loss of accuracy). If the Value is a STRING Type, then an attempt will be made to interpret the String as a long value. Note: The current radix base for the Value will be used when converting a String to a long.

    @return A long value from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is not a NUMERIC Type, or a STRING Type could not be converted to a long.
    @see Long#parseLong(String, int) */ public long long_Data () throws PVL_Exception { if (Is_Numeric ()) { if (_Datum_ == null) return 0L; if (Is_Integer ()) return ((Long)_Datum_).longValue (); return ((Double)_Datum_).longValue (); } if (Is_String ()) { if (_Datum_ == null) return 0L; try { long number = Long.parseLong((String)_Datum_, Math.abs (_Base_)); if (_Base_ < 0) number = -number; return number; } catch (NumberFormatException exception) { throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't create a long value from the " + Type_Name () + " Value \"" + (String)_Datum_ + "\"" ); } } throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a long value from a Value of Type " + Type_Name () + "." ); } /** Gets a Double object from the Value.

    If the Value is Type REAL this is the unmodified datum, unless the datum is null in which case a Double with a 0.0 value is created. If the Value is Type INTEGER then a Double will be created from the Long datum by casting. If the Value is a STRING Type, then an attempt will be made to interpret the String as a Double value.

    @return A Double object from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is not a NUMERIC Type, or a STRING Type could not be converted to a Double.
    @see Double#valueOf(String) */ public Double Double_Data () throws PVL_Exception { if (Is_Numeric ()) { if (_Datum_ == null) return new Double (0.0); if (Is_Real ()) return (Double)_Datum_; return new Double (((Long)_Datum_).doubleValue ()); } if (Is_String ()) { if (_Datum_ == null) return new Double (0.0); try {return Double.valueOf((String)_Datum_);} catch (NumberFormatException exception) { throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't create a Double from the " + Type_Name () + " Value \"" + (String)_Datum_ + "\"" ); } } throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a Double from a Value of Type " + Type_Name () + "." ); } /** Gets a primitive double value from the Value.

    If the Value is Type REAL this is the double value contained in the Double datum, unless the datum is null in which case a value of 0.0 is returned. If the Value is Type INTEGER then a double will be created from the Long datum by casting. If the Value is a STRING Type, then an attempt will be made to interpret the String as a double value.

    @return A double value from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is not a NUMERIC Type, or a STRING Type could not be converted to a double.
    @see Double#parseDouble(String) */ public double double_Data () throws PVL_Exception { if (Is_Numeric ()) { if (_Datum_ == null) return 0.0; if (Is_Real ()) return ((Double)_Datum_).doubleValue (); return ((Long)_Datum_).doubleValue (); } if (Is_String ()) { if (_Datum_ == null) return 0.0; try {return Double.parseDouble((String)_Datum_);} catch (NumberFormatException exception) { throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't create a double value from the " + Type_Name () + " Value \"" + (String)_Datum_ + "\"" ); } } throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a double value from a Value of Type " + Type_Name () + "." ); } /** Gets a String object representing the Value datum.

    If the Value is a STRING Type then the unmodified datum is returned, unless the datum is null in which case an empty String is returned. No special enclosing characters associated with the PVL syntax for specific STRING Types are added (use the toString method for this format).

    If the Value is a NUMERIC Type it is converted to its String representation. Note: For an INTEGER Type Value it's current radix base is used in the conversion (the representation is preceeded by a negative sign if the datum value is negative, which avoids machine precision and byte ordering issues). The radix base value and PVL syntax characters are not included (use the toString method for this format).

    @return A String object from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is an ARRAY Type.
    @see #toString() @see Long#toString(long, int) @see Double#toString() */ public String String_Data () throws PVL_Exception { if (Is_String ()) return (_Datum_ == null) ? "" : (String)_Datum_; if (Is_Integer ()) { long number = (_Datum_ == null) ? 0 : ((Long)_Datum_).longValue (); int base = _Base_; if (base < 0) { base = -base; number = -number; } return Long.toString (number, base); } if (Is_Real ()) return (_Datum_ == null) ? "0.0" : ((Double)_Datum_).toString (); if (Is_Unknown ()) return ""; throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a String from a Value of Type " + Type_Name () + "\n" + " (toString will always return a String)." ); } /** Gets the Value at the specified index of the Array.

    @param index The Array index of the Value to get. @return A Value. If this Value is not an Array, or has no data Vector, null is returned. If this Value is not an Array a PVL_Exception.ILLEGAL_SYNTAX Warning status is set. @see Vector#remove(int) */ public Value Get ( int index ) { if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println (">>> Value.Get: from " + toString () + " at index " + index); Value got = null; if (! Is_Array ()) Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't Get an indexed Value from the Value of Type " + Type_Name () + "." ); if (children != null && index < children.size () && index >= 0) // Get the entry from the data Vector. got = (Value)children.get (index); if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println ("<<< Value.Get - " + ((got == null) ? "(nothing)" : got.toString ())); return got; } /** Gets the data Vector of an Array Value.

    @return The data Vector from the Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    If the Value is not ARRAY Type.
    */ public Vector Vector_Data () throws PVL_Exception { if (Is_Array ()) { if (children == null) children = new Vector (0); return children; } throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't get a Vector from a Value of Type " + Type_Name () + "." ); } /** Gets the number of elements in an Array Value.

    @return The number of Values in the Array. If the Value is not an Array or has no elements, zero will be returned; i.e. returning zero is not determinate that the Value is an Array with an empty list. N.B.: This value does not include the number of Values in any Arrays Values this Array may contain. */ public int Array_Size () { if (Is_Array () && children != null) return children.size (); return 0; } /** Gets the datum object from the Value.

    If the Value is an ARRAY Type, the object will be a Vector. The object will be a Long for an INTEGER Type, a Double for a REAL, or a String for a STRING.

    @return An object of the appropriate class for the Value. */ public Object Data () { if (Is_Array ()) return children; return _Datum_; } /*.............................................................................. Set */ /** Sets the datum of the Value to the Long object, and sets its Type to INTEGER. Note: A null datum is acceptable.

    @param value The new datum for the Value. @return This Value. @see #set_type(int) @see #set_data(Object) */ public Value Data ( Long value ) { set_type (INTEGER); try {set_data (value);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Sets the datum of the Value to the primitive byte value, and sets its Type to INTEGER.

    @param value The new value for the Value. @return This Value. @see #Data(Long) */ public Value Data (byte value) {return Data (new Long ((long)value));} /** Sets the datum of the Value to the primitive short value, and sets its Type to INTEGER.

    @param value The new value for the Value. @return This Value. @see #Data(Long) */ public Value Data (short value) {return Data (new Long ((long)value));} /** Sets the datum of the Value to the primitive int value, and sets its Type to INTEGER.

    @param value The new value for the Value. @return This Value. @see #Data(Long) */ public Value Data (int value) {return Data (new Long ((long)value));} /** Sets the datum of the Value to the primitive long value, and sets its Type to INTEGER.

    @param value The new value for the Value. @return This Value. @see #Data(Long) */ public Value Data (long value) {return Data (new Long (value));} /** Sets the datum of the Value to the Double object, and sets its Type to REAL. Note: A null datum is acceptable.

    @param value The new datum for the Value. @return This Value. @see #set_type(int) @see #set_data(Object) */ public Value Data ( Double value ) { set_type (REAL); try {set_data (value);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** Sets the datum of the Value to the primitive float value, and sets its Type to REAL.

    @param value The new value for the Value. @return This Value. @see #Data(Double) */ public Value Data (float value) {return Data (new Double ((double)value));} /** Sets the datum of the Value to the primitive double value, and sets its Type to REAL.

    @param value The new value for the Value. @return This Value. @see #Data(Double) */ public Value Data (double value) {return Data (new Double (value));} /** Sets the datum of the Value to the String object.

    If the String begins with the {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} character then the Value Type is set to TEXT. If the String begins with the {@link Parser#SYMBOL_DELIMITER Parser.SYMBOL_DELIMITER} character then the Value Type is set to SYMBOL. In either of these cases the leading and trailing (if present) delimiter characters are removed from the String before it is assigned to the datum. Otherwise, if the String contains any {@link Parser#DATE_TIME_DELIMITERS Parser.DATE_TIME_DELIMITERS} the Value Type is set to DATE_TIME. By default the Value Type is set to IDENTIFIER. Note: A null datum is acceptable.

    If the String (after removing any enclosing delimiters) contains any of the {@link Parser#RESERVED_CHARACTERS Parser.RESERVED_CHARACTERS} then a warning status is set. These characters have special meaning in the PVL syntax so writing values containing them could confuse a PVL parser that reads them.

    @param value The new datum for the Value. @return This Value. @see #set_type(int) @see #set_data(Object) */ public Value Data ( String value ) { set_type (IDENTIFIER); // Even if the String value is null. if (value != null && value.length () > 0) { int index; try { if (value.charAt (0) == Parser.TEXT_DELIMITER) { set_type (TEXT); value = value.substring (1); if (value.charAt (value.length () - 1) == Parser.TEXT_DELIMITER) value = value.substring (0, value.length () - 1); } else if (value.charAt (0) == Parser.SYMBOL_DELIMITER) { set_type (SYMBOL); value = value.substring (1); if (value.charAt (value.length () - 1) == Parser.SYMBOL_DELIMITER) value = value.substring (0, value.length () - 1); } else if (value.length () > 0) { for (index = 0; index < Parser.DATE_TIME_DELIMITERS.length (); index++) { if (value.indexOf (Parser.DATE_TIME_DELIMITERS.charAt (index)) >= 0) { set_type (DATE_TIME); break; } } } // Check for reserved characters in the String. if ((index = Parser.Bad_Character (value)) >= 0) Warning ( PVL_Exception.RESERVED_CHARACTER, "At character " + index + " of the Value Data String." ); } catch (IndexOutOfBoundsException exception) {/* No problem */} } try {set_data (value);} catch (PVL_Exception exception) {/* Shouldn't happen */} return this; } /** The primitive char value is used to constructs a single character String which is then set to the datum of the Value.

    The Type of the Value is set according to the rules of the {@link #Data(String) Data (String)} method.

    @param value The new value for the Value. @return This Value. @see #Data(String) */ public Value Data ( char value ) { char character[] = {value}; return Data (new String (character)); } /** Sets the Value data as an Array of Values constructed from the List.

    The Value is classified as the {@link #Default_Array_Type() default Array Type}. A new data Vector is assembled containing Values constructed from the Objects in the list. If any of these Objects are Values they are copied. An Object that is a List results in a new Array Value being recursivley constructed and added to the new data Vector.

    If this Value previously had data, it is replaced with the new Vector of Values. In the case where the previous data was a Vector of Values, all Values of the old Vector are orphaned (they are marked as having no parent).

    @param list The List of values to be adopted. @return This Parameter. @throws PVL_Exception From set_data. @see #set_type(int) @see #set_data(Object) */ public Value Data ( List list ) throws PVL_Exception { set_type (Default_Array_Type); if (list != null) { Iterator entries = list.iterator (); list = new Vector (list.size ()); while (entries.hasNext ()) list.add (new Value (entries.next ())); } set_data (list); return this; } /** Copies the contents of a Value into this Value.

    @param value The Value to be copied into this Value. @return This Value. @throws PVL_Exception From making a copy of the value. @see #Value(Value) @see #graft(Value) */ public Value Data ( Value value ) throws PVL_Exception { // Graft a deep copy of the original Value. graft (new Value (value)); return this; } /** Sets the datum for the Value based on the class of the specified Object.

    There are numerous possibilities:

    Value
    The Value object is copied into this Value using the {@link #Data(Value) Data (Value)} method.
    ARRAY
    The Value is assigned a Vector of Values using the {@link #Data(List) Data (List)} method. The object classes for this case are:
    • List - The List object is used.
    • Value[] - A new Vector is constructed from the array of Values.
    INTEGER
    The Value is assigned a Long using the {@link #Data(Long) Data (Long)} method. The object classes for this case are:
    • Long - The Long object itself is used.
    • Byte - A new Long is constructed from the object's longValue method.
    • Short - A new Long is constructed from the object's longValue method.
    • Integer - A new Long is constructed from the object's longValue method.
    REAL
    The Value is assigned a Double using the {@link #Data(Double) Data (Double)} method. The object classes for this case are:
    • Double - The Double object itself is used.
    • Float - A new Double is constructed from the object's doubleValue method.
    STRING
    The Value is assigned a String using the {@link #Data(String) Data (String)} method. The object classes for this case are:
    • String - The String object itself is used.
    • Character - A String is obtained from the object's toString method.
    • char[ ] - A new String is constructed from the object.
    • byte[ ] - A new String is constructed from the object.
    UNKNOWN
    The object is null so the Value datum will be null.

    @param object The Object to be considered a the Value datum. @return This Value. @throws PVL_Exception

    BAD_ARGUMENT
    The Object is not suitable for a Value datum. */ public Value Data ( Object object ) throws PVL_Exception { if (object == null) { set_type (UNKNOWN); set_data (object); } // Value: else if (Is_Value (object)) Data ((Value)object); // ARRAY: else if (object instanceof List) Data ((List)object); else if (object instanceof Value[]) Data (new Vector (Arrays.asList ((Value[])object))); else if (object instanceof Object[]) Data (new Vector (Arrays.asList ((Object[])object))); // NUMERIC: else if (object instanceof Long) Data ((Long)object); else if (object instanceof Byte) Data (new Long (((Byte)object).longValue ())); else if (object instanceof Short) Data (new Long (((Short)object).longValue ())); else if (object instanceof Integer) Data (new Long (((Integer)object).longValue ())); else if (object instanceof Double) Data ((Double)object); else if (object instanceof Float) Data (new Double (((Float)object).doubleValue ())); // STRING: else if (object instanceof String) Data ((String)object); else if (object instanceof Character) Data (((Character)object).toString ()); else if (object instanceof char[]) Data (new String ((char[])object)); else if (object instanceof byte[]) Data (new String ((byte[])object)); else throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Invalid Data Object for a Value: " + object.getClass ().getName () + "." ); return this; } /*.............................................................................. Modify */ /** Inserts a Value into the Array at the specified index.

    If the Value is not already an Array Type, and does not have a datum, it is given the {@link #Default_Array_Type() default Array Type}. The specified Value is inserted at the specified index in this Value's Array. Any Values in the Array at, or beyond the index are moved to the next index. After the specified Value is inserted its parent is set to this Value. Note: The inserted Value is not removed from any other Array it may be in.

    @param value The Value object to be inserted. @param index The location in the Array for the insertion. @return This Value. @throws PVL_Exception

    ILLEGAL_SYNTAX
    The Value has a non-Array datum.
    @see #Type(int) @see #Remove(Value) @see Vector#insertElementAt(Object, int) */ public Value Insert ( Value value, int index ) throws PVL_Exception { if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println (">>> Value.Insert: " + value + " into " + toString () + " at index " + index); if ((Is_Numeric () || Is_String ()) && _Datum_ != null) throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Can't Insert into the " + Type_Name () + " Value (" + toString () + ")." ); if (value != null) { if (! Is_Array ()) Type (Default_Array_Type); // Adopt it as one of the children. if (children == null) children = new Vector (); try {children.insertElementAt (value, index);} catch (RuntimeException exception) { throw new PVL_Exception ( ID, PVL_Exception.SYSTEM_ERROR, "During Insert at index " + index + " of the " + value.Type_Name () + " Value (" + value + ")\n" + exception.getMessage () ); } value.parent = this; } if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println ("<<< Value.Insert"); return this; } /** Insert an Object constructed as a new Value at the specified index of the Array.

    If the Object is a Value it is not copied.

    @param object The Object whose Value is to be inserted. @param index The location in the Array for the insertion. @return This Value. @throws PVL_Exception From Insert(Value, int). @see #Value(Object) @see #Insert(Value, int) */ public Value Insert ( Object object, int index ) throws PVL_Exception { if (Is_Value (object)) return Insert ((Value)object, index); return Insert (new Value (object), index); } /** Inserts a List of Objects into the Value's Array starting at the specified index.

    The elements of the List are Inserted in the order they occur from the List's listIterator at sequential locations starting with the specified index.

    @param list The List of Objects to Insert. @param index The first of sequential Insert locations. @return This Value. @throws PVL_Exception From the construction of a new Value from an Object. @see #Insert(Object, int) @see List#listIterator() */ public Value Insert ( List list, int index ) throws PVL_Exception { if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println (">>> Value.Insert: List at index " + index); if (list != null) { ListIterator value_list = list.listIterator (); while (value_list.hasNext ()) { /* Try to convert the object into a Value, or make a copy if it is already a Value. */ if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println ("Value.Insert: List object element " + index); Insert (value_list.next (), index++); } } return this; } /** Adds a Value to the end of this Array.

    If this Value is not currently an Array an attempt will be made to convert it to the {@link #Default_Array_Type() default Array Type}. The Insert method is used with the index at the end of the Array.

    N.B.: The {@link #Array_Size() Array size} will only be increased by one even if the Value being added is an Array; in this case the Array Value being added becomes a sub-Array whose parent is this Value. To append the entries of a Value Array to this Value's list of entries add the Value Array's {@link #Vector_Data() list} to this Value.

    @param value The Value to be added. @return This Value. @throws PVL_Exception If Type could not convert the Value to an Array, or there was a problem with the Insert of the value at the end of the Array. @see #Type(int) @see #Insert(Value, int) */ public Value Add ( Value value ) throws PVL_Exception { if (! Is_Array ()) Type (Default_Array_Type); // Try to become an Array. return Insert (value, getChildCount ()); } /** Adds an Object constructed as a new Value to the end of this Array.

    If the Object is a Value it is not copied.

    @param object The Object whose Value is to be inserted. @return This Value. @throws PVL_Exception From Add(Value). @see #Value(Object) @see #Add(Value) */ public Value Add ( Object object ) throws PVL_Exception { if (Is_Value (object)) return Add ((Value)object); return Add (new Value (object)); } /** Adds a List of Objects to the end of the Array.

    If this Value is not currently an Array an attempt will be made to convert it to the {@link #Default_Array_Type() default Array Type}. The Insert method is used with the index at the end of the Array.

    @param list The List of Objects to Insert. @return This Value. @throws PVL_Exception If Type could not convert the Value to an Array, or there was a problem with the Insert of the list at the end of the Array. @see #Type(int) @see #Insert(List, int) */ public Value Add ( List list ) throws PVL_Exception { if (! Is_Array ()) Type (Default_Array_Type); // Try to become an Array. return Insert (list, getChildCount ()); } /** Removes the Value at the specified index from the Array.

    After being removed from the Array the Value is marked as having no parent.

    @param index The Array index of the Value to be removed. @return The Value removed. If this Value is not an Array, or has no Array, null is returned. If this Value is not an Array a PVL_Exception.ILLEGAL_SYNTAX Warning status is set. @see Vector#remove(int) */ public Value Remove ( int index ) { if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println (">>> Value.Remove: from " + toString () + " at index " + index); Value removed = null; if (! Is_Array ()) Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't Remove from the Value of Type " + Type_Name () + "." ); if (children != null) { // Remove the entry from the data Vector. removed = (Value)children.remove (index); // Make it an orphan. removed.parent = null; if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println ("Value.Remove - "); } if ((DEBUG & DEBUG_MODIFY) != 0) System.out.println ("<<< Value.Remove - " + ((removed == null) ? "(nothing)" : removed.toString ())); return removed; } /** Removes the specified Value from the Array.

    The first occurance of the Value in the Array is removed. The Array, if there is one, is searched regardless of the Type of this Value. The search is not recursive: Only this Value's Array data Vector is searched, not the Array Vectors of any Array Values in the Array. The Value is found in the Array by equality of Value object reference, not by the equals method. To remove a Value that equals another Value, use Find and then remove the found Value from its Parent.

    @param value The Value object to be removed. @return The Value removed. If this Value is not an Array, or has no Array, null is returned. If this Value is not an Array a PVL_Exception.ILLEGAL_SYNTAX Warning status is set. @see #Remove(int) */ public Value Remove ( Value value ) { if (value != null && children != null) for (int index = 0; index < children.size (); index++) // Remove the first occurances. if (children.get (index) == value) return Remove (index); return null; } /** Empties the Value's Array.

    The Array data Vector, if it exists, is removed regardless of the Type of this Value. All Values in the Array (but not sub-Arrays) are marked as not having a parent.

    @return This Value. */ public Value Remove_All () { orphan (children); children = null; return this; } /*.............................................................................. */ /** Set the appropriate Data variables.

    The Type of the data object determines how the internal data variables are set. Consistency of the Value's data is ensured by always setting it with this method.

    For Vector data, the data becomes the base class children data Vector. Its contents are adopted which confirms that each element is a Value and its parent is set to this Value. The local datum is set to null.

    For non-ARRAY data, the data becomes the datum of the Value. The children data Vector is orphaned which sets all of its Value elements to have no parent, and then set to null.

    >>> WARNING <<< The Type of the Value is not updated. Accordingly, this method should only be used by methods associated with this package that will also set the Type appropriately.

    @param data The Object to become Value's data. @throws PVL_Exception

    BAD_ARGUMENT
    If the data object is not a valid class for a Value (null is acceptable), or the data object is a Vector and one of its elements is not a Value.
    @see #Type(Object) @see #adopt(Vector) @see #orphan(Vector) */ protected void set_data ( Object data ) throws PVL_Exception { switch (Type (data)) { case UNKNOWN: // The data is null. case INTEGER: case REAL: case STRING: orphan (children); children = null; _Datum_ = data; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("Value.set_data: _Datum_"); break; case ARRAY: /* N.B.: Each DefaultMutableTreeNode contains a parent reference which must be updated when it is moved into a new parent. So transfering a Value list into this Value requires setting the parent of each Value in the list. */ children = (Vector)data; adopt (children); _Datum_ = null; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("Value.set_data: ARRAY"); break; default: throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't set_data of a Value to an object of class " + data.getClass ().getName () + "." ); } } /** Grafts the contents of a Value into this Value.

    In effect, this Value becomes the specified Value.

    The datum reference is set (not copied) to the specified Value's datum reference along with the base, units, and warning status. The Array (children) data Vector reference is set to the specified Value's Vector and if it has contents they are adopted by this Value (the specified Value's children data Vector is set to null to avoid any possible confusion). The Type is set to be the same as the specified Value.

    @param value The Value object to graft into this Value. @throws PVL_Exception From adopt. @see #adopt(Vector) @see #set_type(int) */ protected void graft ( Value value ) throws PVL_Exception { if (value != null) { // Graft the contents of the Value into this Value. _Datum_ = value._Datum_; if ((children = value.children) != null) { // Become the parent of the children. value.children = null; adopt (children); } set_type (value._Type_); _Base_ = value._Base_; _Units_ = value._Units_; _First_Warning_ = value._First_Warning_; _Last_Warning_ = value._Last_Warning_; _Use_First_Warning_ = value._Use_First_Warning_; } } /** Sets the parents of a Vector of Values to this Value.

    >>> WARNING <<<

    Only the parent references for the Values in the Vector are set to this Value. The Vector itself may still be referenced by some other Value. In this case the adoption of the Vector will not be complete until the reference in the former parent is changed (e.g. to null).

    @param vector The Vector of Parameters to adopt. @throws PVL_Exception

    BAD_ARGUMENT
    If an entry in the list is not a Value.
    */ protected void adopt ( Vector vector ) throws PVL_Exception { Object value; if (vector != null) { for (int index = vector.size (); --index >= 0;) { if (Is_Value (value = vector.get (index))) // Set the child's parent to this value. ((Value)value).parent = this; else throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't adopt an object from a List\n" + " element " + index + " is an object of class " + value.getClass ().getName () + "." ); } } } /** Sets a Vector of Values to have no parent.

    >>> WARNING <<<

    Only the parent references for the Values in the Vector are set to null. The Vector itself may still be referenced by some other Value. In this case the orphaning of the Vector will not be complete until the reference in the former parent is changed (e.g. to null).

    @param vector The Vector of Values to orphan. */ static protected void orphan ( Vector vector ) { Object value; if (vector != null) for (int index = vector.size (); --index >= 0;) if (Is_Value (value = vector.get (index))) ((Value)value).parent = null; } /*------------------------------------------------------------------------------ Type: */ /** Gets the Value's Type code.

    @return The Type code. */ public int Type () {return _Type_;} /** Determine the Value Type of an Object.

    The Object is considered to be potentially the datum for a Value and is tested to see if it is an instance of an acceptable Java Class. The object must be an instance of a class suitable for being assigned directly as the datum or data Vector of a Value. Only a basic Type can be determined by examining an object, except for the specific NUMERIC Types. Note: If this method does not provide a valid Type that does not necessarily mean that the object can not be used by the Data (Object) method.

    @param object The Object to be examined. @return The int basic Type code associated with a Value in which the object is its datum. Long objects return the INTEGER code, Double objects the REAL code, String objects the STRING code, and List objects return the ARRAY code. When the object is null, the UNKNOWN code is returned. If an invalid object is specified, then the return value is -1. @see #Data(Object) */ public static int Type ( Object object ) { if (object instanceof Long) return INTEGER; else if (object instanceof Double) return REAL; else if (object instanceof String) return STRING; else if (object instanceof List) return ARRAY; else if (object == null) return UNKNOWN; return -1; } /** Sets the Type of the Value.

    When the Value does not have any data (neither a datum nor a data Vector), or the current Type is UNKNOWN, then the Value is simply set to the specifed Type. When the new Type is UNKNOWN then any existing data is removed with the {@link #set_data(Object) set_data (null)} method and then the Type is set.

    When the current Value Type is the same basic Type as the new Type - i.e. they are both NUMERIC, STRING or ARRAY Types - then the Value is set to the new Type. In the case of a Type change from INTEGER to DOUBLE, or vice versa, the datum is cast to the new Type.

    When the Value is a STRING Type and the new Type is NUMERIC an attempt is made to parse the String using the Value's Double_Data method to produce a REAL Type datum, or the Long_Data method to produce an INTEGER Type datum. In the latter case the current radix base of the Value will be used in the conversion. If the String datum is not a valid representation of the appropriate Type of number then a PVL_Exception.ILLEGAL_SYNTAX will be thrown.

    When the Value is a NUMERIC Type and the new Type is STRING the datum is converted to its String representation using the Value's String_Data method. For an INTEGER Type the current radix base of the Value will be used in the conversion.

    When the Value is to be converted to an ARRAY Type the current Value is {@link #clone() clone}d, the current Value is set to the (empty) {@link #Default_Array_Type() default Array Type}, and the copy of the original Value is {@link #Insert(Value, int) Insert}ed as the first element of the Array. In effect the original Value is "pushed down" as the first and only element of what has become an Array Value; i.e. the new Array contains a copy of the original Value.

    When the Value is an ARRAY Type and the new Type is not, this conversion can only be accomplished if the current Array contains less than two Values. If the Array contains no Values then the new Type is simply set. If the Array contains two or more Values then a PVL_Exception.INCOMPATIBLE_TYPES will be thrown. For an Array with just one Value an attempt is made to convert that Value to the new Type by recursively calling this Type method. If this succeeds then the converted value is {@link #graft(Value) graft}ed into this Value to become the new Type. In effect a single Value contained in an Array is "pulled up" into this Value; i.e. the Array's Value becomes this Value with the new Type.

    @param type The new Type for the Value. @return This Value. @throws PVL_Exception

    INCOMPATIBLE_TYPES
    An Array Value could not be converted to a non-Array value because the Array containes more than one Value, or the Array data Vector unexpectedly contains a non-Value entry.
    ILLEGAL_SYNTAX
    A String datum could not be converted to a number because the String is not a valid represenation of the specified numerical Type.
    BAD_ARGUMENT
    Conversion to or from an unsupported Value Type.
    @see #NUMERIC @see #STRING @see #ARRAY @see #set_type(int) @see Long#doubleValue() @see Double#longValue() @see #clone() @see #graft(Value) @see #Long_Data() @see #Double_Data() @see #String_Data() */ public Value Type ( int type ) throws PVL_Exception { if ((DEBUG & DEBUG_TYPE) != 0) System.out.println (">>> Value.Type: From " + Type_Name () + " to " + Type_Name (type)); if (Data () == null) // There's no data, so just set the new Type. set_type (type); else if (_Type_ != type) { // No change to the basic Type: if ((Is_Array () && Is_Array (type)) || (Is_String () && Is_String (type))) // Decorative change. set_type (type); else if (Is_Numeric () && Is_Numeric (type)) { // Type casting. if (Is_Integer ()) // then type is REAL. _Datum_ = new Double (((Long)_Datum_).doubleValue ()); else // then type is INTEGER. _Datum_ = new Long (((Double)_Datum_).longValue ()); if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Type: cast"); set_type (type); } // Basic Type change: else if (Is_Unknown ()) // From nothing to something. set_type (type); else if (Is_Unknown (type)) { // From something to nothing. set_data (null); // Empty the Value. set_type (type); } else if (Is_Array (type)) { // From a single Value into an Array. Value value = (Value)clone (); orphan (children); children = null; set_type (type); Insert (value, 0); if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Type: cloned into new Array"); } else if (Is_Array ()) { // From an Array into a single Value. if (children.size () == 0) { // Just toss the empty Array. children = null; set_type (type); } else { if (children.size () == 1) { // The Array has only one Value. Object value = children.get (0); if (! Is_Value (value)) throw new PVL_Exception ( ID, PVL_Exception.INCOMPATIBLE_TYPES, "During Value Type change from " + Type_Name () + " to " + Type_Name (type) + "\n" + " an object of class " + value.getClass ().getName () + " was found!" ); /* Convert it to the new non-Array Type. This will delve into any sub-Arrays. */ ((Value)value).Type (type); orphan (children); children = null; // The child has grown up. graft ((Value)value); // It becomes the parent. if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Type: grafted the Array value"); } else throw new PVL_Exception ( ID, PVL_Exception.INCOMPATIBLE_TYPES, "Can't convert the ARRAY Value to Type " + Type_Name (type) + "\n" + " because it contains more than one Value." ); } } else if (Is_String ()) { // From a String to the number it represents. if (Is_Integer (type)) _Datum_ = Long_Data (); else _Datum_ = Double_Data (); set_type (type); if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Type: converted to a number"); } else if (Is_Numeric ()) { // From a number to its String representation. _Datum_ = String_Data (); set_type (type); if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Type: converted to a string"); } else // This shouldn't happen if all the combinations are covered. throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't convert from a Value of Type " + Type_Name () + " to Type " + Type_Name (type) ); } if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("<<< Type"); return this; } /** Sets the appropriate Type variables.

    Consistency of the Value's Type is ensured by always setting it with this method. If the Type code is for an ARRAY, then the allowsChildren variable of the DefaultMutableTreeNode base class will be set to true; otherwise it will be set to false.

    >>> WARNING <<< The data of the Value is not checked for consistency with the Type. Accordingly, this method should only be used by methods associated with this package that will also set the data appropriately.

    @param type The int Type code for the Value. */ protected void set_type ( int type ) { if ((DEBUG & DEBUG_TYPE) != 0) System.out.println ("Value.set_type: from " + Type_Name () + " to " + Type_Name (type)); _Type_ = type & MASK; if (Is_Array ()) allowsChildren = true; else allowsChildren = false; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("Value.set_type: allowsChildren = " + allowsChildren); } /** Gets the name String associated with the Type code.

    Type names are identical to the names of the Type code constants.

    @param type The Type code int. @return The appropriate Type name String. An invalid type code will return a null. */ public static String Type_Name ( int type ) { switch (type) { case INTEGER: return "INTEGER"; case REAL: return "REAL"; case STRING: return "STRING"; case TEXT: return "TEXT"; case IDENTIFIER: return "IDENTIFIER"; case SYMBOL: return "SYMBOL"; case DATE_TIME: return "DATE_TIME"; case ARRAY: return "ARRAY"; case SET: return "SET"; case SEQUENCE: return "SEQUENCE"; case UNKNOWN: return "UNKNOWN"; default: return null; } } /** Gets the name String associated with the Value's Type.

    @return The appropriate Type name String. */ public String Type_Name () {return Type_Name (_Type_);} /** Sets the default Array Value Type to use when an Array is automatically constructed.

    @param type The Array Type to use by default. If this is not an {@link #Is_Array(int) Array Type} nothing is done. @return The previous value. */ public static int Default_Array_Type ( int type ) { int previous_type = Default_Array_Type; if (Is_Array (type)) Default_Array_Type = type; return previous_type; } /** Gets the default Array Value Type to use when an Array is automatically constructed.

    @return The Array Type to use by default. @see #Default_Array_Type(int) */ public static int Default_Array_Type () {return Default_Array_Type;} /*.............................................................................. Type checkers: */ /** Tests if an Object is a Value.

    @param object The Object to be tested. @return true if the object is an instance of the Value class; otherwise false (also false if the object is null). */ public static boolean Is_Value (Object object) {return (object instanceof Value);} /** Tests if an Object is a List suitable for an Array Value.

    Note: The contents of the List are recursively checked to all be Values. null data Vectors are acceptable Values.

    @param object The Object to be tested. @return true if the object is an instance of List and all its elements are Values, or the object is null; otherwise false. @see #Is_Value(Object) @see #Is_Array() */ public static boolean Is_Value_Array (Object object) { if (object == null) return true; if (object instanceof List) { Object value, list; for (int index = ((List)object).size (); --index >= 0;) { if (! Is_Value (value = ((List)object).get (index))) return false; if (((Value)value).Is_Array () && (((Value)value).children != null) && ! Is_Value_Array (((Value)value).children)) return false; } return true; } return false; } /** Tests if the Value is one of the NUMERIC Types.

    NUMERIC Types include INTEGER and REAL Types.

    @return true if the Value has a NUMERIC Type. @see #NUMERIC */ public boolean Is_Numeric () {return ((_Type_ & NUMERIC) != 0);} /** Tests if the Value is the INTEGER Type.

    @return true if the Value is the INTEGER Type. @see #INTEGER */ public boolean Is_Integer () {return (_Type_ == INTEGER);} /** Tests if the Value is the REAL Type.

    @return true if the Value is the REAL Type. @see #REAL */ public boolean Is_Real () {return (_Type_ == REAL);} /** Tests if the Value is one of the STRING Types.

    STRING Types include IDENTIFIER, SYMBOL, TEXT and DATE_TIME Types.

    @return true if the Value has a STRING Type. @see #STRING */ public boolean Is_String () {return ((_Type_ & STRING) != 0);} /** Tests if the Value is the IDENTIFIER Type.

    @return true if the Value is the IDENTIFIER Type. @see #IDENTIFIER */ public boolean Is_Identifier () {return (_Type_ == IDENTIFIER);} /** Tests if the Value is the SYMBOL Type.

    @return true if the Value is the SYMBOL Type. @see #SYMBOL */ public boolean Is_Symbol () {return (_Type_ == SYMBOL);} /** Tests if the Value is the TEXT Type.

    @return true if the Value is the TEXT Type. @see #TEXT */ public boolean Is_Text () {return (_Type_ == TEXT);} /** Tests if the Value is the DATE_TIME Type.

    @return true if the Value is the DATE_TIME Type. @see #DATE_TIME */ public boolean Is_Date_Time () {return (_Type_ == DATE_TIME);} /** Tests if the Value is one of the ARRAY Types.

    ARRAY Types include SET and SEQUENCE Types.

    @return true if the Value has an ARRAY Type. @see #ARRAY */ public boolean Is_Array () {return ((_Type_ & ARRAY) != 0);} /** Tests if the Value is the SET Type.

    @return true if the Value is the SET Type. @see #SET */ public boolean Is_Set () {return (_Type_ == SET);} /** Tests if the Value is the SEQUENCE Type.

    @return true if the Value is the SEQUENCE Type. @see #SEQUENCE */ public boolean Is_Sequence () {return (_Type_ == SEQUENCE);} /** Tests if the Value Type is UNKNOWN (an empty Value).

    @return true if the Value Type is UNKNOWN. @see #UNKNOWN */ public boolean Is_Unknown () {return (_Type_ == UNKNOWN);} // Static versions: /** Tests for a NUMERIC Type.

    @param type The Type code int. @return true if the Type is for a NUMERIC. @see #Is_Numeric() */ public static boolean Is_Numeric (int type) {return ((type & NUMERIC) != 0);} /** Tests for the INTEGER Type.

    @param type The Type code int. @return true if the Type is INTEGER. @see #INTEGER */ public static boolean Is_Integer (int type) {return (type == INTEGER);} /** Tests for the REAL Type.

    @param type The Type code int. @return true if the Type is REAL. @see #REAL */ public static boolean Is_Real (int type) {return (type == REAL);} /** Tests for a STRING Type.

    @param type The Type code int. @return true if the Type is for a STRING. @see #Is_String() */ public static boolean Is_String (int type) {return ((type & STRING) != 0);} /** Tests for the IDENTIFIER Type.

    @param type The Type code int. @return true if the Type is IDENTIFIER. @see #IDENTIFIER */ public static boolean Is_Identifier (int type) {return (type == IDENTIFIER);} /** Tests for the SYMBOL Type.

    @param type The Type code int. @return true if the Type is SYMBOL. @see #SYMBOL */ public static boolean Is_Symbol (int type) {return (type == SYMBOL);} /** Tests for the TEXT Type.

    @param type The Type code int. @return true if the Type is TEXT. @see #TEXT */ public static boolean Is_Text (int type) {return (type == TEXT);} /** Tests for the DATE_TIME Type.

    @param type The Type code int. @return true if the Type is DATE_TIME. @see #DATE_TIME */ public static boolean Is_Date_Time (int type) {return (type == DATE_TIME);} /** Tests for an ARRAY Type.

    @param type The Type code int. @return true if the Type is for an ARRAY. @see #Is_Array() */ public static boolean Is_Array (int type) {return ((type & ARRAY) != 0);} /** Tests for the SET Type.

    @param type The Type code int. @return true if the Type is SET. @see #SET */ public static boolean Is_Set (int type) {return (type == SET);} /** Tests for the SEQUENCE Type.

    @param type The Type code int. @return true if the Type is SEQUENCE. @see #SEQUENCE */ public static boolean Is_Sequence (int type) {return (type == SEQUENCE);} /** Tests for the UNKNOWN Type.

    @param type The Type code int. @return true if the Type is UNKNOWN. @see #UNKNOWN */ public static boolean Is_Unknown (int type) {return (type == UNKNOWN);} /*------------------------------------------------------------------------------ Base: */ /** Gets the radix base that will be applied if this Value is an INTEGER Type.

    @return The current radix base int associated with this Value. @see #Base(int) */ public int Base () {return _Base_;} /** Sets the radix base to be applied if this Value is an INTEGER Type during a conversion between (to or from) a Long and String representation.

    The radix base is associated with the Value regardless of whether it is currently an INTEGER Type and remains associated with the Value regarless of Type changes. Valid radix base values are in the range Character.MIN_RADIX <= |base| <= Character.MAX_RADIX. The sign of the radix base value will be applied to the Value datum when the radix base is used, but is otherwise ignored. For example, if a negative radix base is applied to positive INTEGER Value, then long_Data will return a positive long value but String_Data will return the represenation of the negative of the long value since a conversion has been applied. Using negative radix base numbers can be a source of confusion and probably should be avoided.

    @param base The radix base int to be used. @return This Value. @throws PVL_Exception

    VALUE_OVERFLOW
    The value of the radix base out of range.
    @see Character#MIN_RADIX @see Character#MAX_RADIX */ public Value Base ( int base ) throws PVL_Exception { if (Math.abs (base) < Character.MIN_RADIX || Math.abs (base) > Character.MAX_RADIX) throw new PVL_Exception ( ID, PVL_Exception.VALUE_OVERFLOW, "Invalid Value Base " + base + ".\n" + " Acceptable range: " + Character.MIN_RADIX + " <= |Base| <= " + Character.MAX_RADIX ); _Base_ = base; return this; } /*------------------------------------------------------------------------------ Units: */ /** Get the units description String.

    @return The units description String, which may be null. */ public String Units () {return _Units_;} /** Set the units description String.

    This is an arbitrary String that is expected to provide some form of real world units description for the Value. It may be null to remove any units description.

    @param units The units description String. @return This Value. */ public Value Units ( String units ) { _Units_ = units; return this; } /*------------------------------------------------------------------------------ Parent: */ /** Gets the Value that contains this Value as an Array element.

    @return The Array that contains this Value in its data Vector, or null if this Value is not contained in an Array. */ public Value Parent () {return (Value)getParent ();} /*------------------------------------------------------------------------------ Warning: */ /** Gets the current warning status.

    When conditions are encountered that are unusual enough to warrant attention, but not an error condition that would prevent successful processing which would cause an exception to be thrown, a warning condition is registered. The warning is in the form of a PVL_Exception that was not thrown. The current warning status is either the {@link #First_Warning(boolean) First_Warning} or the {@link #Last_Warning(boolean) Last_Warning} since a {@link #Reset_Warning() Reset_Warning}.

    @return The current warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #First_Warning(boolean) @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Warning () { if (_Use_First_Warning_) return _First_Warning_; return _Last_Warning_; } /** Clears any warning status so that the Warning method will return null until the next warning condition occurs.

    @return This Value. @see #Warning() */ public Value Reset_Warning () { _First_Warning_ = null; _Last_Warning_ = null; return this; } /** Enables or disables returning the first warning that occurs as the current warning status.

    The first warning is one that occurs when the current warning status is null.

    @param first true to enable returning the first warning status; false to return the last warning that occurred as the current warning status. @return This Value. @see #Warning() @see #First_Warning() @see #Reset_Warning() */ public Value First_Warning ( boolean first ) { _Use_First_Warning_ = first; return this; } /** Returns the first warning since the last Reset_Warning.

    @return The first warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #First_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception First_Warning () {return _First_Warning_;} /** Enables or disables returning the last warning that occurs as the current warning status.

    The last warning is the most recent one regarless of any previous warning conditions that may have occured without an intervening Reset_Warning.

    @param last true to enable returning the last warning status; false to return the first warning condition that occurred as the current warning status. @return This Value. @see #Warning() @see #Last_Warning() @see #Reset_Warning() */ public Value Last_Warning ( boolean last ) { _Use_First_Warning_ = ! last; return this; } /** Returns the last warning since a Reset_Warning.

    @return The last warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Last_Warning () {return _Last_Warning_;} /*============================================================================== General Methods */ /** Makes a deep copy of the Value.

    @return An Object of class Value that is a copy of this Value. @see #Value(Value) @see Object#clone() @see Cloneable */ public Object clone () { try {return new Value (this);} catch (PVL_Exception execption) {return null;} } /** Gets a String representation of the Value.

    For an INTEGER Type Value with a decimal radix base the Long datum's toString representation is returned. If the datum is null a 0L value is provided. When the radix base is not decimal then the radix base is incuded in the representation using PVL syntax:

    [ sign ] base#value#

    Where the negative sign is used if either the radix base or the datum value (but not both) are negative, the radix base is represented in decimal notation, and the datum value is represented in the character set corresponding to the radix base. The crosshatch characters are the {@link Parser#NUMBER_BASE_DELIMITER Parser.NUMBER_BASE_DELIMITER} character.

    For a REAL Type Value the Double datum's toString representation is returned. If the datum is null a "0.0" representation is returned.

    For STRING Types the String datum is returned (the empy string is used for a null datum) enclosed in any PVL syntax characters appropriate for its specific Type. For the TEXT Type the {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} character starts and ends the String, while for the SYMBOL Type the {@link Parser#SYMBOL_DELIMITER Parser.SYMBOL_DELIMITER} is used.

    For Values with an ARRAY Type, the Type_Name is used as a place holder (use the Write method to get all of the individual Array entries). For the SET Type this is enclosed in {@link Parser#SET_START_DELIMITER Parser.SET_START_DELIMITER} and {@link Parser#SET_END_DELIMITER Parser.SET_END_DELIMITER} characters. For the SEQUENCE Type the {@link Parser#SEQUENCE_START_DELIMITER Parser.SEQUENCE_START_DELIMITER} and {@link Parser#SEQUENCE_END_DELIMITER Parser.SEQUENCE_END_DELIMITER} characters enclose the name.

    For Values with the UNKNOWN Type just the Type_Name is provided.

    @return A String description of the Value datum plus any units. @see #Write(OutputStream, int, boolean) @see #Type_Name() @see Long#toString() @see Double#toString() */ public String toString () { switch (_Type_) { case INTEGER: int sign = (_Base_ < 0) ? -1 : 1, base = Math.abs (_Base_); long number = (_Datum_ == null) ? 0 : ((Long)_Datum_).longValue (); if (number < 0) { // Always use a positive number. sign = -sign; number = -number; } if (base == 10) return Long.toString (sign * number); else return String.valueOf (sign * base) + Parser.NUMBER_BASE_DELIMITER + Long.toString (number, base).toUpperCase () + Parser.NUMBER_BASE_DELIMITER; case REAL: return (_Datum_ == null) ? "0.0" : ((Double)_Datum_).toString (); case STRING: case IDENTIFIER: case DATE_TIME: return (_Datum_ == null) ? "" : (String)_Datum_; case TEXT: return Parser.TEXT_DELIMITER + ((_Datum_ == null) ? "" : (String)_Datum_) + Parser.TEXT_DELIMITER; case SYMBOL: return Parser.SYMBOL_DELIMITER + ((_Datum_ == null) ? "" : (String)_Datum_) + Parser.SYMBOL_DELIMITER; case ARRAY: return Type_Name (); case SET: return Parser.SET_START_DELIMITER + Type_Name () + Parser.SET_END_DELIMITER; case SEQUENCE: return Parser.SEQUENCE_START_DELIMITER + Type_Name () + Parser.SEQUENCE_END_DELIMITER; case UNKNOWN: return Type_Name (); default: return null; } } /** Gets a PVL description of the Value using a specified Lister.

    The Value is {@link #Write(OutputStream) Write}n to a StringWriter on the specified Lister and the resultant String is returned. If the Write method produces an exception, the exception message is returned. The Lister will have its Writer {@link Lister#Set_Writer(Writer) set} to the StringWriter used to write the PVL syntax. If no Lister is specified (the argument is null) then a default Lister will be provided.

    @param lister The Lister to use when writing the PVL syntax. @return The String describing the Value in PVL syntax. */ public String Description ( Lister lister ) { StringWriter writer = new StringWriter (); if (lister == null) lister = new Lister (writer); else lister.Set_Writer (writer); try {lister.Write (this);} catch (Exception exception) {writer.write ("\n" + exception.getMessage ());} String description = writer.toString (); if (description.charAt (description.length () - 1) == '\n') description = description.substring (0, description.length () - 1); return description; } /** Gets a PVL description of the Value.

    The Value is {@link #Write(OutputStream) Write}n to a StringWriter on a default Lister and the resultant String is returned. If the Write method produces an exception, the exception message is returned.

    @return The String describing the Value in PVL syntax. @see #Description(Lister) */ public String Description () {return Description (null);} /** Tests if a Value matches this Value using the specified criteria.

    @param value The Value to be compared to this Value. @param criteria The Selector providing the comparison criteria. @return true if there is a criteria match; false otherwise. @see Selector */ public boolean Match ( Value value, Selector criteria ) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (">>> Value.Match"); boolean match = criteria.Values_Match (this, value); if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Value.Match: " + match); return match; } /** Tests if a Value matches this Value using the specified criteria.

    If the Value is an Array matching is done recursively on all Values of the Array. The match succeeds if all Values match.

    N.B.: To avoid unnecessary redundant recursive comparisons of Array data values if the {@link Selector#Data(boolean) Data} criteria is included in the criteria the {@link Selector#Data_Match(Value, Value) data comparison} is only done for non-Array Values.

    @param value The Value to be compared to this Value. @param criteria The Selector providing the comparison criteria. @return true if there is a criteria match; false otherwise. @see Selector @see Selection */ public boolean Match_Depth ( Value value, Selector criteria ) { boolean matches, match_data = criteria.Data (); if (match_data) // Defer Data_Match to non-Array values. criteria.Data (false); matches = match_depth (value, criteria, match_data); if (match_data) criteria.Data (true); return matches; } private boolean match_depth ( Value value, Selector criteria, boolean match_data ) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (">>> Value.match_depth:\n" +" this - " + this.toString () + '\n' +" value - " + value + '\n' +" " + criteria + '\n' +" match_data - " + match_data); if (value == null || ! criteria.Values_Match (this, value)) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Value.match_depth: false"); return false; } if ( this.Is_Array () && value.Is_Array ()) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" Comparing Array Lists ..."); try { if (this.Vector_Data ().size () != value.Vector_Data ().size ()) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" this list size " + this.Vector_Data ().size () + " != value list size " + value.Vector_Data ().size () + '\n' +"<<< Value.match_depth: false"); return false; } } catch (PVL_Exception exception) { // Shouldn't happen since they're both valid Arrays. return false; } Iterator these_values = this.iterator (), those_values = value.iterator (); while (these_values.hasNext () && those_values.hasNext ()) { if (! ((Value)these_values.next ()).match_depth ((Value)those_values.next (), criteria, match_data)) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Value.match_depth: false"); return false; } } } else if (match_data) { if ((DEBUG & DEBUG_MATCH) != 0) System.out.println (" Comparing data values ..."); boolean matches = criteria.Data_Match (this, value); if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Value.match_depth: " + matches); return matches; } if ((DEBUG & DEBUG_MATCH) != 0) System.out.println ("<<< Value.match_depth: true"); return true; } /** Tests if an Object is equal to this Value.

    An Object is equal to this Value when it is also a Value object and all of its fields are equal to this Value's fields. A Selection object with VALUE_MATCH criteria is used with the Match_Depth method.

    @param object The Object to be compared to this Value. @return true if the object equals this Value, false otherwise. @see #Match_Depth(Value, Selector) @see Selection @see Selector#VALUE_MATCH */ public boolean equals ( Object object ) { if (! Is_Value (object)) return false; return Match_Depth ((Value)object, new Selection (Selector.VALUE_MATCH)); } /** Tests if a Value is equivalent to this Value, ignoring certain cases where field values are not equal.

    Unlike equals, for a Value to be equivalent to this Value the fields need only be logically equivalent, rather than containing the same value. A Selection object with VALUE_MATCH but Specific (false) criteria is used with the Match_Depth method.

    @param value The Value to be compared to this Value. @return true if the Value is logically equivalent to this Value, false otherwise. @see #Match_Depth(Value, Selector) @see Selection @see Selector#VALUE_MATCH @see Selection#Specific(boolean) */ public boolean equalsIgnoreCase ( Value value ) {return Match_Depth (value, new Selection (Selector.VALUE_MATCH).Specific (false));} /*------------------------------------------------------------------------------ */ /** Writes the Value in PVL syntax using a PVL Lister.

    The Lister is created with the output stream as its list destination using the specified indent level and syntax strictness.

    @param output The OutputStream to receive what is written. If output is null System.out is used. @param level The indent level at which to start the listing. If the level is negative, indenting is disabled. @param strict Use strict PVL syntax if true; otherwise use an easier to read format. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an Array is not a Value.
    ILLEGAL_SYNTAX
    If a PVL reserved character was found in an unenclosed STRING datum during strict writing.
    @throws IOException From the OutputStream write method. @see Lister#Write(Value) */ public int Write ( OutputStream output, int level, boolean strict ) throws PVL_Exception, IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("\n>>> Value.Write"); Lister lister = new Lister (output) .Indent_Level (level) .Strict (strict); int total = lister.Write (this); Warning (lister.Warning ()); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Value.Write: total written = " + total); return total; } /** Writes the Value in PVL syntax to System.out starting at level 0, not strict.

    @return The total number of bytes written. @throws PVL_Exception From the foundation Write method. @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write () throws PVL_Exception, IOException {return Write (System.out, 0, false);} /** Writes the Value in PVL syntax to System.out with or without indenting, not strict.

    @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @return The total number of bytes written. @throws PVL_Exception From the foundation Write method. @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( boolean indent ) throws PVL_Exception, IOException {return Write (System.out, (indent ? 0 : -1), false);} /** Writes the Value in PVL syntax to output System.out with indenting and strict PVL enabled or disabled.

    @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @param strict Use strict PVL syntax if true; otherwise use an easier to read format. @return The total number of bytes written. @throws PVL_Exception From the foundation Write method. @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( boolean indent, boolean strict ) throws PVL_Exception, IOException {return Write (System.out, (indent ? 0 : -1), strict);} /** Writes the Value in PVL syntax to the output starting at level 0, not strict.

    @param output The OutputStream to receive what is written. @return The total number of bytes written. @throws PVL_Exception From the foundation Write method. @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( OutputStream output ) throws PVL_Exception, IOException {return Write (output, 0, false);} /** Writes the Value in PVL syntax to the output with or without indenting, not strict.

    @param output The OutputStream to receive what is written. @param indent Enable indenting starting at level 0 if true; disable indenting otherwise. @return The total number of bytes written. @throws PVL_Exception From the foundation Write method. @throws IOException From the OutputStream write method. @see #Write(OutputStream, int, boolean) */ public int Write ( OutputStream output, boolean indent ) throws PVL_Exception, IOException {return Write (output, (indent ? 0 : -1), false);} /*------------------------------------------------------------------------------ Iterators: */ /** Provides an Iterator for Values contained in an Array positioned so that the next element returned will be from the specified index of the Array.

    This Iterator does not descend into nested Array Values. The {@link DefaultMutableTreeNode DefaultMutableTreeNode} base class provdes various Enumeration objects that will traverse the hierarchy in different orders.

    For non-Array Values an "empty" Iterator will be provided. This will act the same as an Iterator on an empty data Vector.

    @param index The starting list element. @return An Iterator object. @see Iterator @see DefaultMutableTreeNode */ public Iterator iterator ( int index ) { if (Is_Array ()) return new Value_Iterator (index); return Parameter.EMPTY_LIST_ITERATOR; } /** Provides an Iterator starting at the first element of the list.

    @return An Iterator object. @see #iterator(int) */ public Iterator iterator () {return iterator (0);} /** Provides a ListIterator for Values contained in an Array positioned so that the next element returned will be from the specified index of the list.

    The functionality of the Value Iterator is provided (which it extends) with the ListIterator interface implemented.

    Note: When the Value is an ARRAY Type, but has no data Vector, an empty one is provided so the add method will be able to insert new Values into the Array.

    @param index The starting list element. @return A ListIterator object. @see #iterator() @see ListIterator */ public ListIterator listIterator ( int index ) { if (Is_Array ()) return new Value_ListIterator (index); return Parameter.EMPTY_LIST_ITERATOR; } /** Provides a ListIterator starting at the first element of the list.

    @return A ListIterator object. @see #listIterator(int) */ public ListIterator listIterator () {return listIterator (0);} /*............................................................................. */ private class Value_Iterator implements Iterator { ListIterator list_iterator; Value last = null; private Value_Iterator () {this (0);} private Value_Iterator ( int index ) { if (children == null) children = new Vector (); // May need to add to this aggregate. list_iterator = children.listIterator (index); } public boolean hasNext () {return list_iterator.hasNext ();} public Object next () { if (hasNext ()) return (last = (Value)list_iterator.next ()); throw new NoSuchElementException (ID); } public void remove () { if (last != null) { list_iterator.remove (); last.parent = null; last = null; return; } throw new IllegalStateException (ID); } } // End of Value_Iterator class private class Value_ListIterator extends Value_Iterator implements ListIterator { private Value_ListIterator ( int index ) {super (index);} public boolean hasPrevious () {return list_iterator.hasPrevious ();} public Object previous () {return (last = (Value)list_iterator.previous ());} public int nextIndex () {return list_iterator.nextIndex ();} public int previousIndex () {return list_iterator.previousIndex ();} public void set ( Object object ) { if (! Is_Value (object)) throw new ClassCastException (ID + "\nThe Object to set is not a Value."); if (last != null) { list_iterator.set (object); ((Value)object).parent = last.parent; last.parent = null; last = (Value)object; return; } throw new IllegalStateException (ID); } public void add ( Object object ) { if (! Is_Value (object)) throw new ClassCastException (ID + "\nThe Object to set is not a Parameter."); list_iterator.add (object); ((Value)object).parent = Value.this; last = null; } public void insert ( Value value ) {add (value);} } // End of Value_ListIterator class /*------------------------------------------------------------------------------ Find */ /** Searches for a Value that matches a test Value according the Selector criteria, optionally after some specific Value object has been located first.

    A search is made of the Values contained in this Array, and, recursively, any Arrays it contains. The search is depth-wise: each Array Value is searched when it is encountered.

    The search must first find the last_value (having the same object reference) before proceeding with the search for the test_value beginning with the Value following the last_value. If the last_value is null, then the search for the test_value begins with the first Value contained in this Array.

    The search compares each value encountered against the test_value using the Selector criteria's Values_Match method. The first Value that matches the test_value is returned. If the last_value or the test_value is not found, null is returned. If the last_value could not be found a PVL_Exception.NO_LAST_LOCATION warning status is registered with this Value. If the Value in which to Find is not an ARRAY Type, null will be returned and a PVL_Exception.ILLEGAL_SYNTAX warning status will be set.

    The test_value may be any parameter, pre-existing or created ad hoc as a template for the Selector criteria. A Selector offers numerous criteria that may be conditionally applied when comparing the test_value with Array members. Note: The PIRL.PVL package provides a Selection class that implements the Selector interface.

    @param test_value A Value object to use for testing Values in this Array for a match. @param criteria A Selector object providing the methods to determine if Values match. @param last_value A Value object to be found within the Array before search comparisons begin. If null, begin the search at the first element of the Array. @return The Value object that was found, or null if not found. If the Value in which to Find is not an ARRAY Type, null will be returned and a PVL_Exception.ILLEGAL_SYNTAX warning status will be set. If the last_value could not be found a PVL_Exception.NO_LAST_LOCATION warning status is registered with this Value. @see Selector @see Selection#Values_Match(Value, Value) */ public Value Find ( Value test_value, Selector criteria, Value last_value ) { if (! find_check (test_value)) return null; Value[] Last_Value = {last_value}; if ((DEBUG & DEBUG_FIND) != 0) { System.out.println ( ">>> Value Find: " + test_value.Type_Name () + " " + test_value + "\n" + " criteria = " + Integer.toString (criteria.Criteria (), 2) + "\n" + " Data = " + criteria.Data () + "\n" + " Type = " + criteria.Type () + "\n" + " Base = " + criteria.Base () + "\n" + " Units = " + criteria.Units () + "\n" + " Specific = " + criteria.Specific () + "\n" + " AND = " + criteria.And () ); if (last_value != null) System.out.println (" last_value is " + last_value); } test_value = find (test_value, criteria, Last_Value); if (test_value == null && Last_Value[0] != null && Warning () == null) Warning ( PVL_Exception.NO_LAST_LOCATION, "Can't Find the last Value (" + last_value + ")." ); return test_value; } /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(byte) @see #Find(Value, Selector, Value) */ public Value Find (byte value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(short) @see #Find(Value, Selector, Value) */ public Value Find (short value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(int) @see #Find(Value, Selector, Value) */ public Value Find (int value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(long) @see #Find(Value, Selector, Value) */ public Value Find (long value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(float) @see #Find(Value, Selector, Value) */ public Value Find (float value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the primitive value.

    A test_value is constructed from the primitive value and a selection criteria requiring a data match is provided.

    @param value The primitive value for the datum being sought. @return The Value object that was found, or null if not found. @see #Value(double) @see #Find(Value, Selector, Value) */ public Value Find (double value) {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds a Value having the datum equal to the value of the Object.

    A test_value is constructed from the object and a selection criteria requiring a data match is provided.

    @param value The object having the value for the datum being sought. @return The Value object that was found, or null if not found. @throws PVL_Exception If a Value could not be constructed from the Object. @see #Value(Object) @see #Find(Value, Selector, Value) */ public Value Find (Object value) throws PVL_Exception {return Find (new Value (value), (new Selection ()).Data (true));} /** Finds the Value of the specified Type, beginning after the specified Value object is found.

    The Type matching is specific. For a general Type match - i.e. where any two NUMERIC, STRING or ARRAY Values will match - use:

    Find (new Value ().Type (type),
          new Selection ().Type (true).Specific (false),
          last_value);
    

    @param type The Type code to search for. @param last_value A Value object to be found within the Array before search comparisons begin. If null, begin the search at the first element of the Array. @return The Value object that was found, or null if not found. @see #Find(int) @see #Find(Value, Selector, Value) @see Selection#Type(boolean) @see Selection#Specific(boolean) */ public Value Find ( int type, Value last_value ) { Value value = new Value (); value.set_type (type); return Find ( value, new Selection ().Type (true).Specific (true), last_value ); } /** Finds the Value that completely matches a test Value.

    A complete match requires an exact match of the Value data and its Type (as well as the numeric base for integers), and any units String. Note: Value contents, not Value object references, are compared.

    @param test_value The Value to use when testing for a match. @return The Value object that was found, or null if not found. @see #Find(Value, Selector, Value) */ public Value Find ( Value test_value ) { return Find ( test_value, new Selection ().Criteria (Selector.VALUE_MATCH), null ); } /** Finds the Value that completely matches a test Value, beginning after the specified Value within the Array.

    @param test_value A Value object to use for testing Values in this Array for a match. @param last_value A Value object to be found within the Array before search comparisons begin. If null, begin the search at the first element of the Array. @return The Value object that was found, or null if not found. @see #Find(Value) @see #Find(Value, Selector, Value) */ public Value Find ( Value test_value, Value last_value ) { return Find ( test_value, new Selection ().Criteria (Selector.VALUE_MATCH), last_value ); } /** Finds the Value that matches a test Value according the Selector criteria.

    @param test_value A Value to use for testing Values in this Array for a match. @param criteria A Selector object providing the methods to determine if Values match. @return The Value object that was found, or null if not found. @see #Find(Value, Selector, Value) */ public Value Find ( Value test_value, Selector criteria ) {return Find (test_value, criteria, null);} private boolean find_check ( Value test_value ) { // So last value not found will be noticed. Reset_Warning (); if (! Is_Array ()) { Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't Find in a Value of Type " + Type_Name () + "." ); return false; } if (test_value == null || children == null) return false; return true; } private Value find ( Value Test_Value, Selector Criteria, Value[] Last_Value ) { /* An array is used for Last_Value to provide a pass-back value when the Last_Value[0] has been located. */ if ((DEBUG & DEBUG_FIND) != 0) System.out.println (">>> find"); Value value, found_value = null; /* Find the Test_Value. Note: The algorithm of this function searches depth-wise; i.e. each value encountered in the Array is searched (conditionally) when it is encountered. */ Iterator values = iterator (); while (values.hasNext ()) { value = (Value)values.next (); if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("find: " + value.Type_Name () + " " + value); if (Last_Value[0] == null) { /* Compare the value with the Test_Value using the Criteria. Note that the Test_Value comparison only occurs after the Last_Value has been found (or isn't being used). */ if (Criteria.Values_Match (value, Test_Value)) { found_value = value; if ((DEBUG & DEBUG_FIND) != 0) System.out.println (" Matched"); break; } } else { // Just searching for the Last_Value. if (value == Last_Value[0]) Last_Value[0] = null; // Found it. } if (value.Is_Array () && (found_value = value.find ( Test_Value, Criteria, Last_Value )) != null) break; } if ((DEBUG & DEBUG_FIND) != 0) System.out.println ("<<< Value find: found_value is " + (found_value != null)); return found_value; } /*============================================================================== Methods that override base class methods: */ /* Errors are reported as a Warning. It would be better if there were a way to tell the caller directly about problems. Unfortunately, the flawed design of the base class methods does not provide any means to safely report back to the caller, not even a function return value. */ /** Overrides the base class method to ensure Value integrity.

    @param object Since the userObject of a Value is a reference to itself, it is illegal to use this method. A PVL_Exception.BAD_ARGUMENT warning status is set. @see DefaultMutableTreeNode#setUserObject(Object) */ public void setUserObject ( Object object ) { Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't setUserObject of a Value." ); } /** Overrides the base class method to ensure Value integrity.

    @param allows Since the ability of a Value to have children is totally determined by its Type (only Arrays may have a children data Vector), this argument is ignored and a PVL_Exception.ILLEGAL_SYNTAX warning status is set. @see DefaultMutableTreeNode#setAllowsChildren(boolean) */ public void setAllowsChildren ( boolean allows ) { Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Can't setAllowsChildren of a Value.\n" + " This is determined by the Value's Type." ); } /** Invokes the Value's Add method.

    @param value A MutableTreeNode that must be a Value object. @throws IllegalArgumentException If the value argument is not a Value object, or the Add method threw a PVL_Exception (in which case it's message is used for the exception message). @see #Add(Value) @see DefaultMutableTreeNode#add(MutableTreeNode) */ public void add ( MutableTreeNode value ) throws IllegalArgumentException { if (! Is_Value (value)) throw new IllegalArgumentException ( ID + "\n" + "The argument to add is not a Value." ); try {Add ((Value)value);} catch (PVL_Exception exception) {throw new IllegalArgumentException (exception.getMessage ());} } /** Invokes the Value's Insert method.

    @param value A MutableTreeNode that must be a Value object. @param index The index in the Array where the Value is to be inserted. @throws IllegalArgumentException If the value argument is not a Value object, or the Insert method threw a PVL_Exception (in which case it's message is used for the exception message). @see #Insert(Value, int) @see DefaultMutableTreeNode#insert(MutableTreeNode, int) */ public void insert ( MutableTreeNode value, int index ) throws IllegalArgumentException { if (! Is_Value (value)) throw new IllegalArgumentException ( ID + "\n" + "The argument to insert is not a Value." ); try {Insert ((Value)value, index);} catch (PVL_Exception exception) {throw new IllegalArgumentException (exception.getMessage ());} } /** Invokes the Value's Remove method.

    @param index The index of the element in the Array to remove. @see #Remove(int) @see DefaultMutableTreeNode#remove(int) */ public void remove ( int index ) {Remove (index);} /** Invokes the Value's Remove method.

    @param value A MutableTreeNode that must be a Value object. @throws IllegalArgumentException If the value argument is not a Value object. @see #Remove(Value) @see DefaultMutableTreeNode#remove(MutableTreeNode) */ public void remove ( MutableTreeNode value ) throws IllegalArgumentException { if (! Is_Value (value)) throw new IllegalArgumentException ( ID + "\n" + "The argument to remove is not a Value." ); Remove ((Value)value); } /*------------------------------------------------------------------------------ */ private void Warning ( String description, String explanation ) { Warning ( new PVL_Exception ( ID, description, explanation ) ); } private void Warning ( PVL_Exception warning ) { if (warning != null) { _Last_Warning_ = warning; if (_First_Warning_ == null) _First_Warning_ = _Last_Warning_; } } } // End of class pirl-2.3.8/PIRL/PVL/PVL_to_DB0000755000175000017500000000645111070331106015207 0ustar mathieumathieu#!/bin/csh -f # # CVS ID: PVL_to_DB,v 1.3 2008/09/30 04:59:50 castalia Exp # # PVL_to_DB # # A wrapper for the PIRL Java Database PVL to database mapper. # # see http://pirlwww.lpl.arizona.edu/software/PIRL_Java_Packages/PIRL/PVL/PVL_to_DB.html # # Environment variables used and their default values: # # PIRL_JAVA_HOME - /opt/java # This may be a directory pathname where the PIRL subdirectory and # all its class files is located; or it may be the pathname to the # PIRL.jar file containing all dependencies. On Darwin systems the # ~/Library/Java/Extensions and /Library/Java/Extensions directory # will be checked for the PIRL.jar file. # # The following are used only if PIRL_JAVA_HOME is not a jar file. # # MySQL_JDBC - $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # The pathname to the MySQL JDBC driver jar file or the root # directory where its class files are located. # (http://dev.mysql.com/downloads/connector/j/) # # PostgreSQL_JDBC - $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. # (http://jdbc.postgresql.org/) # # JCM - $PIRL_JAVA_HOME/jcm/jcm_data.jar # The pathname to the Java Components for Mathematics jar file or # the root directory where its class files are located. # (http://math.hws.edu/javamath) # # CVS ID: PVL_to_DB,v 1.3 2008/09/30 04:59:50 castalia Exp set OS = `uname -s` # Location of the PIRL Java Packages. if (! $?PIRL_JAVA_HOME) then if ($OS == "Darwin") then if (-e ~/Libarary/Java/Extensions/PIRL.jar) then set PIRL_JAVA_HOME = ~/Libarary/Java/Extensions/PIRL.jar else if (-e /Libarary/Java/Extensions/PIRL.jar) then set PIRL_JAVA_HOME = /Libarary/Java/Extensions/PIRL.jar endif endif endif if (! $?PIRL_JAVA_HOME) \ set PIRL_JAVA_HOME = /opt/java endif if (! -e ${PIRL_JAVA_HOME}) then echo "No such file or directory: ${PIRL_JAVA_HOME}" exit -1 endif set classpath = $PIRL_JAVA_HOME if ($PIRL_JAVA_HOME !~ "*.jar") then # Database drivers: # Location of the MySQL JDBC driver. if (! $?MySQL_JDBC) \ set MySQL_JDBC = $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar if (! -e ${MySQL_JDBC}) then echo "No such file or directory: ${MySQL_JDBC}" echo "MySQL database support is not available." unset MySQL_JDBC unsetenv MySQL_JDBC else set classpath = ${classpath}:${MySQL_JDBC} endif # Location of the PostgreSQL JDBC driver. if (! $?PostgreSQL_JDBC) \ set PostgreSQL_JDBC = $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar if (! -e ${PostgreSQL_JDBC}) then echo "No such file or directory: ${PostgreSQL_JDBC}" echo "PostgreSQL database support is not available." unset PostgreSQL_JDBC unsetenv PostgreSQL_JDBC else set classpath = ${classpath}:${PostgreSQL_JDBC} endif if (! $?MySQL_JDBC && ! $?PostgreSQL_JDBC) then echo "Conductor can not function without database support." exit 1 endif # External Java packages: # Location of the Java Components for Mathematics. if (! $?JCM) \ set JCM = $PIRL_JAVA_HOME/jcm/jcm_data.jar if (! -e ${JCM}) then echo "No such file or directory: ${JCM}" echo "The Java Components for Mathematics are required by Conductor." echo "See http://math.hws.edu/javamath" exit -1 endif set classpath = ${classpath}:${JCM} endif exec java -cp $classpath PIRL.PVL.PVL_to_DB $argv:q pirl-2.3.8/PIRL/PVL/Lister.java0000644000175000017500000013057011742734277015704 0ustar mathieumathieu/* Lister PIRL CVS ID: Lister.java,v 1.20 2012/04/16 06:14:23 castalia Exp Copyright (C) 2002-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; import java.io.Writer; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.StringWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ListIterator; import PIRL.Strings.String_Buffer; /** A Lister writes PVL syxtax.

    A Lister complements a Parser in that the latter interprets Parameter Value Language (PVL) syntax from a Reader into Parameter and Value objects, while the former generates PVL syntax to a Writer.

    @see Parser @see Parameter @see Value @author Bradford Castalia, UA/PIRL @version 1.20 */ public class Lister { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Lister (1.20 2012/04/16 06:14:23)"; /*------------------------------------------------------------------------------ PVL syntax modes: */ /** The default for enforcing {@link #Strict(boolean) strict} PVL syntax rules. */ public static boolean Strict_Default = false; private boolean _Strict_ = Strict_Default; /** {@link #Verbatim_Strings(boolean) Verbatim strings} default. */ public static boolean Verbatim_Strings_Default = false; private boolean _Verbatim_Strings_ = Verbatim_Strings_Default; /** The default {@link #Indent_Level(int) statement base indent level} (none if negative). */ public static int Indent_Level_Default = 0; private int _Aggregate_Indent_Level_ = Indent_Level_Default; /** The default for applying {@link #Indent_Arrays(boolean) array indenting}. */ public static boolean Indent_Arrays_Default = true; private boolean _Array_Indent_ = Indent_Arrays_Default; // Output destination. private Writer _Writer_ = null; /** The indent character(s).

    This String is written once for each {@link #Indent_Level(int) indent level}. The default value is the tab character. A suitable alternative could be four space characters. */ public static String INDENT = "\t"; /** The platform's native line separator (newline) String.

    This String is obtained from System.getProperty ("line.separator"), or "\n" is used if the System property can't be obtained. */ public static String NEW_LINE; static { NEW_LINE = System.getProperty ("line.separator"); if (NEW_LINE == null) NEW_LINE = "\n"; } // When throwing an exception is inappropriate. private PVL_Exception _First_Warning_ = null, _Last_Warning_ = null; /** The default for returning the {@link #First_Warning(boolean) first warning} (or {@link #Last_Warning(boolean) last warning}). */ public static boolean First_Warning_Default = true; private boolean _Use_First_Warning_ = First_Warning_Default; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_PARAMETER = 1 << 1, DEBUG_VALUE = 1 << 2, DEBUG_COMMENTS = 1 << 3, DEBUG_WRITE = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Lister using a Writer as the destination for PVL statements.

    @param writer The Writer destination for characters. @see #Set_Writer(Writer) */ public Lister ( Writer writer ) {Set_Writer (writer);} /** Creates a Lister using an OutputStream as the destination for PVL statements.

    @param output_stream The OutputStream destination for characters. @throws PVL_Exception From Set_Writer @see #Set_Writer(OutputStream) */ public Lister ( OutputStream output_stream ) throws PVL_Exception {Set_Writer (output_stream);} /** Creates a Lister using a File as the destination for PVL statements.

    @param file The File destination for characters. @throws PVL_Exception From Set_Writer @see #Set_Writer(File) */ public Lister ( File file ) throws PVL_Exception {Set_Writer (file);} /** Creates a Lister with the default (System.out) destination for PVL statements. */ public Lister () {Set_Writer ((Writer)null);} /*------------------------------------------------------------------------------ */ /** Sets the Writer where the Lister will send characters.

    N.B.: The US-ASCII character encoding is used.

    @param writer The Writer destination for Lister output. If null, then a BufferedWriter constructed from an OutputStreamWriter using System.out is provided. @return This Lister. @throws IllegalStateException If the {@link Parser#CHARACTER_ENCODING} (US-ASCII) is not supported. */ public Lister Set_Writer ( Writer writer ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Lister.Set_Writer"); _Writer_ = writer; if (_Writer_ == null) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Using default Writer"); try {_Writer_ = new BufferedWriter (new OutputStreamWriter (System.out, Parser.CHARACTER_ENCODING));} catch (UnsupportedEncodingException exception) { IllegalStateException throwable = new IllegalStateException (ID + NEW_LINE + "Unable to use the " + Parser.CHARACTER_ENCODING + " character encoding."); throwable.initCause (exception); throw throwable; } } return this; } /** Gets the current destination for Lister characters.

    @return The current Writer for the Lister. @see #Set_Writer(Writer) */ public Writer Get_Writer () {return _Writer_;} /** Sets the Writer where the Lister will send characters by constructing an OutputStreamWriter from the specified OutputStream and wrapping this in a BufferedWriter for efficiency.

    N.B.: The US-ASCII character encoding is used.

    @param output_stream The OutputStream destination for Lister output. If null, System.out is used. @return This Lister. @throws IllegalStateException If the {@link Parser#CHARACTER_ENCODING} (US-ASCII) is not supported. */ public Lister Set_Writer ( OutputStream output_stream ) { if (output_stream == null) output_stream = System.out; try {Set_Writer (new BufferedWriter (new OutputStreamWriter (output_stream, Parser.CHARACTER_ENCODING)));} catch (UnsupportedEncodingException exception) { IllegalStateException throwable = new IllegalStateException (ID + NEW_LINE + "Unable to use the " + Parser.CHARACTER_ENCODING + " character encoding."); throwable.initCause (exception); throw throwable; } return this; } /** Sets the Writer where the Lister will send characters by constructing a FileWriter from the specified File and wrapping this in a BufferedWriter for efficiency.

    @param file The File destination for Lister output. @return This Lister. @throws PVL_Exception A FILE_IO error condition will occur if the File refers to a directory or a file for which write permission is not provided. Any other IOException that occurs while constructing the FileWriter will also be converted into a PVL_Exception.FILE_IO exception. */ public Lister Set_Writer ( File file ) throws PVL_Exception { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Lister.Set_Writer: File " + file.getName ()); if (file.isDirectory ()) throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "The File pathname is for a directory - " + file.getAbsolutePath () ); if (! file.canWrite ()) throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Can't write the file - " + file.getAbsolutePath () ); try {Set_Writer (new BufferedWriter (new FileWriter (file)));} catch (IOException exception) { throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Can't access the file - " + file.getAbsolutePath () + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } return this; } /*============================================================================== Accessors: */ /** Enables or disables strict PVL syntax rules in the Lister.

    The default is controlled by the {@link #Strict_Default} value.

    @param strict true if strict rules are applied; false otherwise. @return This Lister. */ public Lister Strict ( boolean strict ) { _Strict_ = strict; return this; } /** Tests if the Lister will enforce strict PVL syntax rules.

    @return true if the Lister will enforce strict syntax rules; false otherwise. */ public boolean Strict () {return _Strict_;} /** Enable or disable verbatim quoted strings.

    Verbatim strings are listed exactly as they occur.

    With verbatim strings disabled Parameter names, STRING Values, and units description strings will have special characters translated to escape sequences. However, in this case those sections of these strings that are bracketed by {@link Parser#VERBATIM_STRING_DELIMITERS VERBATIM_STRING_DELIMITERS} will not be translated, though the VERBATIM_STRING_DELIMITERS will be removed.

    The default is controlled by the {@link #Verbatim_Strings_Default} value.

    @param verbatim true if strings are to be written verbatim; false if special character translation is to be applied. @return This Lister. @see #translate_to_escape_sequences(String_Buffer) */ public Lister Verbatim_Strings ( boolean verbatim ) { _Verbatim_Strings_ = verbatim; return this; } /** Tests if verbatim strings mode is enabled.

    @return true if verbatim strings mode is enabled; false otherwise. @see #Verbatim_Strings(boolean) */ public boolean Verbatim_Strings () {return _Verbatim_Strings_;} /** Sets the base indenting level when listing PVL statements.

    The default base level is 0. However, the default is controlled by the {@link #Indent_Level_Default} value. Aggregate Parameter contents and subarrays of array Values are idented an additional level from their containing statement listing. If the base level is negative, indenting is disabled.

    The default base level is 0. However, the default is controlled by the {@link #Indent_Level_Default} value.

    @param level The base indenting level. @return This Lister. @see #Indent(boolean) @see #Indent_Arrays(boolean) */ public Lister Indent_Level ( int level ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Lister.Indent_Level: "+ level); _Aggregate_Indent_Level_ = level; return this; } /** Gets the current base indent level.

    @return The current base indent level. @see #Indent_Level(int) */ public int Indent_Level () {return _Aggregate_Indent_Level_;} /** Enables or disables indenting of PVL statement listings.

    When enabling indenting, if indenting was previously disabled the default (0) base level is set. However, if indenting was previously enabled the current level remains unchanged.

    @param indent true to enable indenting; false to disable it. @return This Lister. @see #Indent_Level(int) */ public Lister Indent ( boolean indent ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Lister.Indenting: " + indent); if (indent) { if (_Aggregate_Indent_Level_ < 0) _Aggregate_Indent_Level_ = 0; } else _Aggregate_Indent_Level_ = -1; return this; } /** Enables or disables indenting of Array Value listings.

    Normally indenting of Array Values is controlled by the indent level of PVL statement listings; with the listing of the Array, and each Array Value within the Array, starting on the next line at the next indent level. If indenting for PVL statements (i.e. Aggregate Parameters) is disabled, Arrays will be indented from level 0. However, Array indenting may be disabled even though PVL statement indenting remains enabled, in which case all Values of an Array will be written on one line.

    The default is controlled by the {@link #Indent_Arrays_Default} value.

    @param indent true to enable Array indenting; false to disable it. @return This Lister. @see #Indent(boolean) @see #Indent_Level(int) */ public Lister Indent_Arrays ( boolean indent ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Lister.Indent_Arrays: " + indent); _Array_Indent_ = indent; return this; } /** Tests if Array Values will be indented.

    @return true if Arrays Values will be indented; false otherwise. @see #Indent_Arrays(boolean) */ public boolean Indent_Arrays () {return _Array_Indent_;} /** Gets the current warning status.

    When conditions are encountered that are unusual enough to warrant attention, but not an error condition that would prevent successful processing which would cause an exception to be thrown, a warning condition is registered. The warning is in the form of a PVL_Exception that was not thrown. The current warning status is either the {@link #First_Warning(boolean) First_Warning} or the {@link #Last_Warning(boolean) Last_Warning} since a {@link #Reset_Warning() Reset_Warning}.

    @return The current warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #First_Warning(boolean) @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Warning () { if (_Use_First_Warning_) return _First_Warning_; return _Last_Warning_; } /** Clears any warning status so that the Warning method will return null until the next warning condition occurs.

    @return This Lister. @see #Warning() */ public Lister Reset_Warning () { _First_Warning_ = null; _Last_Warning_ = null; return this; } /** Enables or disables returning the first warning that occurs as the current warning status.

    The first warning is one that occurs when the current warning status is null. If the use of the first warning is disabled the last warning that occurred will be returned instead. The default is to use the first warning. However, the default is controlled by the {@link #First_Warning_Default} value.

    N.B.: Enabling the return of the first warning is the same as disabling the {@link #Last_Warning(boolean) return of the last warning}.

    @param first true to enable returning the first warning status; false to return the last warning that occurred as the current warning status. @return This Lister. @see #Last_Warning(boolean) @see #Warning() @see #First_Warning() @see #Reset_Warning() */ public Lister First_Warning ( boolean first ) { _Use_First_Warning_ = first; return this; } /** Returns the first warning since the last Reset_Warning.

    @return The first warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #First_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception First_Warning () {return _First_Warning_;} /** Enables or disables returning the last warning that occurs as the current warning status.

    The last warning is the most recent one that ocurred, regardless of any previous warning conditions that may have occured, without an intervening Reset_Warning.

    N.B.: Enabling the return of the last warning is the same as disabling the {@link #First_Warning(boolean) return of the first warning}.

    @param last true to enable returning the last warning status; false to return the first warning condition that occurred as the current warning status. @return This Lister. @see #First_Warning(boolean) @see #Warning() @see #Last_Warning() @see #Reset_Warning() */ public Lister Last_Warning ( boolean last ) { _Use_First_Warning_ = ! last; return this; } /** Returns the last warning since a Reset_Warning.

    @return The last warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Last_Warning () {return _Last_Warning_;} /*============================================================================== Write: */ /** Writes the Parameter in PVL syntax.

    The PVL syntax that is written follows this pattern:

    Comments
    Name = Value

    The comments string, if there is one, is written by the Write_Comments method.

    The Parameter's name is written next beginning on a new line. If indenting is enabled, the name will be preceeded by the current indent count number of horizontal tab characters. The name String itself is usually provided by the Parameter's Name method. However, if the Parser's Special_Name method determines that the Parameter's classification is associated with a special name (as it is for all aggregate and END Parameters), then that will be written instead. Unless {@link #Verbatim_Strings Verbatim_Strings} are enabled, escape sequences are substituted for special (format control) characters and {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER}, {@link Parser#SYMBOL_DELIMITER Parser.SYMBOL_DELIMITER} and backslash ('\') characters are escaped (with a preceding backslash character). If the name contains {@link Parser#WHITESPACE whitespace} or {@link Parser#RESERVED_CHARACTERS reserved} characters or is empty then it will be quoted (with {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} characters). Note: When Strict PVL has been specified, Parameter's that begin an aggregate list will have the special name BEGIN_GROUP or BEGIN_OBJECT, otherwise the BEGIN_ portion will not be included.

    The Parameter's value is written next, if the Parameter is not a token or classified as UNKNOWN. It is preceeded by the {@link Parser#PARAMETER_NAME_DELIMITER equals} character surrounded by space characters. For an assignment, the Write Value) method completes the output of a parameter, including any line termination, and the method returns. If there is no Value, an empty quoted String is written by default. For aggregates, the name of the Parameter is its value; i.e. in this case the special name for the specific classification substitutes for the usual name, and the usual name appears as the value (yes, this seems strange, but take it up with the committee that wrote the specification for the PVL syntax).

    The Parameter line is completed by writing a new-line character. However, if Strict PVL has been specified, then the {@link Parser#STATEMENT_END_DELIMITER Parser.STATEMENT_END_DELIMITER} character followed by the {@link Parser#LINE_BREAK Parser.LINE_BREAK} sequence is written instead.

    When the Parameter being written is an aggregate, the indent level (if indenting is enabled) is incremented and then each Parameter in the aggregate list is recursively written. At the end of the list the indent level is decremented (if indenting is enabled) and an END_GROUP or END_OBJECT Parameter, corresponding to the classification of the aggregate containing the list, is written. If an END Parameter is encountered in the aggregate list, then the list is terminated at that point. If an END_PVL Parameter is encountered, then the current list is terminated as well as the lists of all containing aggregates; i.e. all Parameter lists end at that point.

    As a special case, when the Parameter being written is an aggregate with the name {@link Parser#CONTAINER_NAME Parser.CONTAINER_NAME} this "container" Parameter is not written, but the Parameters in it's aggregate list are written normally. This special case only applies to the first Parameter inspected by the Write (Parameter) method, not aggregate list entries; i.e. only the top of the hierarchy is considered as a potential special case.

    Normally the special END_PVL Parameter is not written at the end of the listing. This is so that several Parameters can be written to a file before the application provides the END_PVL Parameter formally expected at the end of a PVL sequence. However, when the Parameter being written is the top of the output hierarchy and has the special Parser.CONTAINER_NAME, then the END_PVL Parameter is automatically provided to reflect the end of the container's parameters; i.e. what is written should be logically the equivalent to what was read by the Parser.

    @param parameter The Parameter to write. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an aggregate list is not a Parameter.
    @throws IOException From the Writer's write method. @see #Write_Comments(String, int) @see Parameter#Name() @see Parser#Special_Name(int) @see #translate_to_escape_sequences(String_Buffer) @see #Write(Value) */ public int Write ( Parameter parameter ) throws PVL_Exception, IOException { if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (">>> Lister.Write (Parameter)"); /* Flag when an END_XXX Parameter is encountered. Normally END Parameters are not included in Parameter lists; they are implicit at the end of the list. However, a user might decide to provide them; for example to manually mark the end of an existing list (or because it is considered formally correct for some application dependent reason). So when they are encountered processing of a Parameter list stops. There are two kinds of END Parameters: One that ends an aggregate Parameter list (Is_End_Aggregate) ends the output of Parameters in the current aggregate. One that ends all PVL statements (Is_End_PVL) for all nested aggregates. An array is used to provide a pass-back value: 0 - No END 1 - END_AGGREGATE -1 = END_PVL */ int[] end = {0}; int total = write (parameter, _Aggregate_Indent_Level_, true, end); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("<<< Lister.Write (Parameter): total written = " + total); return total; } /* The method that controls Parameter writing. The interface is designed to provide for recursion with start and end information. */ private int write ( Parameter parameter, int level, boolean start, int[] end ) throws PVL_Exception, IOException { if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (">>> Lister.write (Parameter): level " + level); int total_written = 0; boolean top_level_container = ( start && parameter.Is_Aggregate () && /* The aggregate a Parser may create to hold all input Parameters. This is an "artificial" Parameter that did not exist in the input stream, so only its contents are written. If, however, the user renames this Parameter, then it is written. */ parameter.Name ().equals (Parser.CONTAINER_NAME) ); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (" Lister.write: top_level_container = " + top_level_container); // Comments: Write_Comments (parameter.Comments (), level); if (! top_level_container) { /* Name: */ String name; total_written += indent (level); // Check for special names for special classifications. if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (" Lister.write: Classification " + parameter.Classification_Name ()); if ((name = Parser.Special_Name (parameter.Classification ())) == null) name = parameter.Name (); else if (_Strict_) { if (! name.toUpperCase ().startsWith ("BEGIN_")) name = "BEGIN_" + name; } else if (name.toUpperCase ().startsWith ("BEGIN_")) name = name.substring (name.indexOf ('_') + 1); total_written += write_name (name); /* Data: */ if (! parameter.Is_Token () && ! parameter.Is_Unknown ()) { total_written += write (' '); total_written += write (Parser.PARAMETER_NAME_DELIMITER); total_written += write (' '); if (parameter.Is_Aggregate ()) // The Parameter name is the value of aggregates. total_written += write_name (parameter.Name ()); else if (parameter.Value () != null) /* The output of the Value completes the Parameter output. This includes the PVL statement termination. */ return total_written += write (parameter.Value (), (level < 0 || top_level_container) ? level : level + 1, true ); else total_written += write_name (null); } /* PVL statement termination. */ if (_Strict_) total_written += write (Parser.STATEMENT_END_DELIMITER); total_written += new_line (); } if (parameter.Is_Begin_Aggregate ()) { // Recursively write each Parameter in the aggregate. if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (" Lister.write: writing aggregate parameters."); if (parameter.List () != null) { ListIterator list = parameter.List ().listIterator (); while (list.hasNext ()) { Object object = list.next (); if (! Parameter.Is_Parameter (object)) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "In the AGGREGATE Parameter named \"" + parameter.Name () + "\"\n" + " element " + list.previousIndex () + " is an object of class " + object.getClass ().getName () + "." ); // User specified end. if (((Parameter)object).Is_End ()) { if (((Parameter)object).Is_End_Aggregate ()) end[0] = 1; else end[0] = -1; break; } total_written += write ( (Parameter)object, (level < 0 || top_level_container) ? level : level + 1, false, end ); if (end[0] < 0) break; } } total_written += indent (level); if (! start || ! top_level_container) { // Add the end aggregate Parameter. total_written += write_name (Parser.Special_Name (parameter.Classification () | Parameter.END)); if (_Strict_) total_written += write (Parser.STATEMENT_END_DELIMITER); } if (top_level_container || (start && end[0] < 0)) { // The END. if (! start || ! top_level_container) // Add the NL after the preceeding end of aggregate token. total_written += new_line (); total_written += write_name (Parser.Special_Name (Parameter.END_PVL)); if (_Strict_) total_written += write (Parser.STATEMENT_END_DELIMITER); } total_written += new_line (); } if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("<<< Lister.write (Parameter): total_written = " + total_written); return total_written; } /** Writes a PVL comments sequence.

    Normally comments are attached to a Parameter with the Comments method and they are written as part of the Parameter. Comments may be inserted virtually anywhere in a PVL listing, and this method is provided for such ad hoc commenting.

    Comments are delimited by {@link Parser#COMMENT_START_DELIMITERS Parser.COMMENT_START_DELIMITERS} and {@link Parser#COMMENT_END_DELIMITERS Parser.COMMENT_END_DELIMITERS} (C-style) sequences. When Strict PVL syntax is specified each line of the comments String (a line being delmited by a new-line character) is separated into individual single line comments. Otherwise the comments String is simply output between the start and end sequences as-is.

    If indenting is enabled each comment start sequence will be preceeded by the current indent count number of horizontal tab characters. Unless Strict syntax has been specified, the comment end sequence is preceeded by a new-line character and any indenting. If indenting is enabled the end of the comments sequence is completed by writing a new-line character, or the {@link Parser#LINE_BREAK Parser.LINE_BREAK} sequence for Strict PVL.

    @param comments The String to be written in a comments sequence. If comments is null, nothing is written. @param level The indent level to use. @return The total number of bytes written. @throws IOException From the OutputStream write method. */ public int Write_Comments ( String comments, int level ) throws IOException { if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println (">>> Lister.Write_Comments: level " + level); int total_written = 0; if (comments != null) { if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println (" Lister.Write_Comments: " + comments); int count = 0, length = comments.length (); do { // Prefix. total_written += indent (level); total_written += write (Parser.COMMENT_START_DELIMITERS); if (count < length && comments.charAt (count) != '\n') total_written += write (" "); // Body. char character = 0; while (count < length) { if ((character = comments.charAt (count++)) == '\n' && _Strict_) break; if (character == '\n') { total_written += new_line (); if (count < length && comments.charAt (count) != '\n') { total_written += indent (level); for (int shift = Parser.COMMENT_START_DELIMITERS.length () + 2; shift > 0; --shift) total_written += write (' '); } } else total_written += write (character); } // Postfix. if (level >= 0 && ! _Strict_) { // Must be at the end of the comments string. total_written += new_line (); total_written += indent (level); } total_written += write (Parser.COMMENT_END_DELIMITERS); if (level >= 0) /* Only when indenting. Turn off indenting for in-line comments. */ total_written += new_line (); } while (count < length); } if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println ("<<< Lister.Write_Comments: total written = " + total_written); return total_written; } /** Writes a PVL comments sequence at the current indent level.

    @param comments The String to be written in a comments sequence. If comments is null, nothing is written. @return The total number of bytes written. @throws IOException From the Writer's write method. @see #Write_Comments(String, int) */ public int Write_Comments ( String comments ) throws IOException {return Write_Comments (comments, _Aggregate_Indent_Level_);} /*.............................................................................. */ private int write_name ( String name ) throws PVL_Exception, IOException { if (name == null) name = ""; int index; String_Buffer representation = new String_Buffer (name); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (">>> Lister.write_name: " + name); if (! _Verbatim_Strings_) translate_to_escape_sequences (representation); if (representation.length () == 0) { // An empty name. Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Empty Parameter name." ); if (_Strict_) throw _Last_Warning_; // Add quotes. representation .append (Parser.TEXT_DELIMITER) .append (Parser.TEXT_DELIMITER); } else if ((index = representation.skip_until (0, Parser.RESERVED_CHARACTERS)) >= 0) { if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (" Lister.write_name: reserved character in name."); // There's a reserved character in the String. Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Reserved character at index " + index + " of the Parameter named \"" + representation.toString () + "\"" ); if (_Strict_) throw _Last_Warning_; // Add quotes to protect the reserved characters. representation .insert (0, Parser.TEXT_DELIMITER) .append (Parser.TEXT_DELIMITER); } write (representation.toString ()); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("<<< Lister.write_name: total written = " + representation.length () + " - " + representation); return representation.length (); } /*------------------------------------------------------------------------------ */ /** Writes the Value in PVL syntax.

    The PVL syntax that is written follows this pattern:

    [ ( | { ] Datum [ < Units > ] [ , Datum [...] ] [ ) | } [ < Units > ] ]

    The Value datum is always written. If the Value is the UNKNOWN type then an empty SYMBOL type is provided by default. If the datum is a STRING type and {@link #Verbatim_Strings Verbatim_Strings} are disabled, escape sequences are substituted for special (format control) characters and {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER}, {@link Parser#SYMBOL_DELIMITER Parser.SYMBOL_DELIMITER} and backslash ('\') characters are escaped (with a preceding backslash character). If the string contains {@link Parser#WHITESPACE whitespace} or {@link Parser#RESERVED_CHARACTERS reserved} characters or is empty then it will be quoted (with {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} characters).

    If the Value is an ARRAY type then the appropriate enclosure start character ({@link Parser#SET_START_DELIMITER Parser.SET_START_DELIMITER} or {@link Parser#SEQUENCE_START_DELIMITER Parser.SEQUENCE_START_DELIMITER}) is written before each Value in the array Vector is recursively written by this method with a space and {@link Parser#PARAMETER_VALUE_DELIMITER Parser.PARAMETER_VALUE_DELIMITER} separation. After all the array Vector Values have been written the appropriate enclosure end character ({@link Parser#SET_END_DELIMITER Parser.SET_END_DELIMITER} or {@link Parser#SEQUENCE_END_DELIMITER Parser.SEQUENCE_END_DELIMITER}) is written.

    After each Value is written, whether a single datum or an array, it is followed by any units description String (preceeded by a space character and enclosed in PVL syntax characters ({@link Parser#UNITS_START_DELIMITER Parser.UNITS_START_DELIMITER} and {@link Parser#UNITS_END_DELIMITER Parser.UNITS_END_DELIMITER}).

    Array indenting may be enabled. In this mode each nested array is written on separate lines indented by tab characters to its nesting level. Each subarray starts a new line and increments the nesting level. The end of a subarray is followed by a new-line and the nesting level is decremented before continuing a previous array level (but not if there is no previous array level or the previous level has no more Values). The start of an array at level 0 is not given indent treatment.

    In Strict mode new-line sequences are the {@link Parser#LINE_BREAK Parser.LINE_BREAK} characters; normally the native new-line (the line.separator System property) is used. Also, Strict mode results in the {@link Parser#STATEMENT_END_DELIMITER Parser.STATEMENT_END_DELIMITER} character preceeding the usual new-line that completes the Value output.

    If an unenclosed STRING type datum contains any of the {@link Parser#RESERVED_CHARACTERS Parser.RESERVED_CHARACTERS} then a warning status is set (and thrown in Strict mode). These characters have special meaning in the PVL syntax so writing values containing them could confuse a PVL parser that reads them. Unless strict mode is in effect, the string is enclosed in {@link Parser#TEXT_DELIMITER Parser.TEXT_DELIMITER} characters to protect them from inappropriate interpretation.

    @param value The Value to write. @return The total number of bytes written. @throws PVL_Exception

    BAD_ARGUMENT
    If an element in an array is not a Value.
    ILLEGAL_SYNTAX
    If a PVL reserved character was found in an unenclosed STRING datum during Strict writing.
    @throws IOException From the OutputStream write method. @see #translate_to_escape_sequences(String_Buffer) */ public int Write ( Value value ) throws PVL_Exception, IOException { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("\n>>> Lister.Write (Value)"); int total = write (value, _Aggregate_Indent_Level_, true); if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("<<< Lister.Write (Value): total written = " + total); return total; } private int write ( Value value, int level, boolean start ) throws PVL_Exception, IOException { int total_written = 0; if (value.Is_Array ()) { // Array listing: if (! _Array_Indent_) level = -1; // Indenting disabled. else if (level < 0) level = 0; if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("\n Lister.write (Value): writing array values at level " + level + "."); if (level > 0) { // Start an indented array on the next line. total_written += new_line (); total_written += indent (level); } if (value.Is_Set ()) total_written += write (Parser.SET_START_DELIMITER); else total_written += write (Parser.SEQUENCE_START_DELIMITER); // Recursively output each value in the array. boolean previous_array = false; ListIterator values = value.listIterator (); while (values.hasNext ()) { Object object = values.next (); if (! Value.Is_Value (object)) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "While Writing element " + values.previousIndex () + " of a " + value.Type_Name () + " Value\n" + " an object of class " + object.getClass ().getName () + " was found." ); if (previous_array && level >= 0 && ! ((Value)object).Is_Array ()) { /* The previous value was an array and the current value is not an array so reposition back to the current indent level. */ total_written += new_line (); total_written += indent (level); } // Write the array Value. total_written += write ( (Value)object, (level < 0) ? level : (level + 1), false // Within an array. ); if (values.hasNext ()) { total_written += write (Parser.PARAMETER_VALUE_DELIMITER); previous_array = ((Value)object).Is_Array (); if (! previous_array || level < 0) total_written += write (' '); } } if (value.Is_Set ()) total_written += write (Parser.SET_END_DELIMITER); else total_written += write (Parser.SEQUENCE_END_DELIMITER); } else { // Fundamental Value listing: String_Buffer representation; int index; if (value.Is_String ()) { representation = new String_Buffer (value.String_Data ()); if (! _Verbatim_Strings_) translate_to_escape_sequences (representation); if (value.Is_Text ()) representation .insert (0, Parser.TEXT_DELIMITER) .append (Parser.TEXT_DELIMITER); else if (value.Is_Symbol ()) representation .insert (0, Parser.SYMBOL_DELIMITER) .append (Parser.SYMBOL_DELIMITER); else if ((index = representation.skip_until (0, Parser.RESERVED_CHARACTERS)) >= 0) { // There's a reserved character in the String. Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Reserved character at index " + index + " of the " + value.Type_Name () + " value \"" + representation + "\"" ); if (_Strict_) throw _Last_Warning_; // Add quotes to protect the reserved characters. representation .insert (0, Parser.TEXT_DELIMITER) .append (Parser.TEXT_DELIMITER); } } else if (value.Is_Unknown ()) representation = new String_Buffer (""); else representation = new String_Buffer (value.toString ()); if (representation.length () == 0) // An empty representation needs to be quoted. representation .insert (0, Parser.TEXT_DELIMITER) .append (Parser.TEXT_DELIMITER); total_written += write (representation.toString ()); } // Units: total_written += write_units (value.Units ()); if (start) { /* PVL statement termination. */ if (_Strict_) total_written += write (Parser.STATEMENT_END_DELIMITER); total_written += new_line (); } if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("\n<<< Lister.write (Value): total_written = " + total_written); return total_written; } /*.............................................................................. */ private int write_units ( String units ) throws PVL_Exception, IOException { if (units == null) return 0; int index; String_Buffer representation = new String_Buffer (units); if (! _Verbatim_Strings_) translate_to_escape_sequences (representation); if ((index = representation.skip_until (0, Parser.RESERVED_CHARACTERS)) >= 0) { // There's a reserved character in the String. Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Reserved character at index " + index + " of units \"" + representation + "\"" ); if (_Strict_) throw _Last_Warning_; } return write ( representation .insert (0, ' ') .insert (1, Parser.UNITS_START_DELIMITER) .append (Parser.UNITS_END_DELIMITER) .toString () ); } /*------------------------------------------------------------------------------ Primitive writers */ private int indent ( int level ) throws IOException { String string = ""; while (level-- > 0) string += INDENT; return write (string); } private int new_line () throws IOException { String EOL; if (_Strict_) EOL = Parser.LINE_BREAK; else EOL = NEW_LINE; write (EOL); _Writer_.flush (); return EOL.length (); } private int write ( String string ) throws IOException { if (string == null) return 0; if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">-< Lister.write: " + string.length () + " character string -\n" + ">|" + string + "|<"); _Writer_.write (string); return string.length (); } private int write ( int character ) throws IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">-< Lister.write: character >|" + (char)character + "|<"); _Writer_.write (character); return 1; } /*------------------------------------------------------------------------------ */ /** Translates special characters in a String_Buffer to their corresponding escape sequences.

    In addition to the special characters translated by the {@link String_Buffer#special_to_escape() special_to_escape} method, the {@link Parser#TEXT_DELIMITER TEXT_DELIMITER} and {@link Parser#SYMBOL_DELIMITER SYMBOL_DELIMITER} characters are also escaped. The occurance of {@link Parser#VERBATIM_STRING_DELIMITERS VERBATIM_STRING_DELIMITERS} starts a sequence of characters that are taken verbatim (they are not translated) up to and including the next VERBATIM_STRING_DELIMITERS or the end of the string.

    @param string The String_Buffer to be translated. @return The translated String_Buffer. @see String_Buffer#special_to_escape() */ public static String_Buffer translate_to_escape_sequences ( String_Buffer string ) { if (string == null) return string; int first = 0, last, length, delimiters_length = Parser.VERBATIM_STRING_DELIMITERS.length (); String_Buffer section; // Find sections delimited by VERBATIM_STRING_DELIMITERS. for (last = string.index_of (first, Parser.VERBATIM_STRING_DELIMITERS); first < string.length (); last = string.index_of (first = last, Parser.VERBATIM_STRING_DELIMITERS)) { if (last < 0) // This section extends to the end of the string. last = string.length (); section = new String_Buffer (string.substring (first, last)); length = section.length (); // Convert special characters to escape sequences. section .special_to_escape () .replace (0, new Character (Parser.TEXT_DELIMITER).toString (), "\\" + Parser.TEXT_DELIMITER) .replace (0, new Character (Parser.SYMBOL_DELIMITER).toString (), "\\" + Parser.SYMBOL_DELIMITER); if (section.length () != length) { // The section changed; replace it in the string. string.replace (first, last, section.toString ()); last = first + section.length (); } // Skip the next, verbatim, section. if ((last += delimiters_length) >= string.length () || (last = string.index_of (last, Parser.VERBATIM_STRING_DELIMITERS)) < 0 || (last += delimiters_length) == string.length ()) // No more sections. break; } return string; } /*------------------------------------------------------------------------------ */ private void Warning ( String description, String explanation ) { _Last_Warning_ = new PVL_Exception ( ID, description, explanation ); if (_First_Warning_ == null) _First_Warning_ = _Last_Warning_; } } // class Lister pirl-2.3.8/PIRL/PVL/Parser.java0000644000175000017500000030002511742734277015670 0ustar mathieumathieu/* Parser PIRL CVS ID: Parser.java,v 1.35 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.Reader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.Character; import java.lang.Long; import java.lang.Double; import java.lang.Math; import java.util.Vector; import PIRL.Strings.String_Buffer; import PIRL.Strings.String_Buffer_Reader; /** The Parser extends the String_Buffer_Reader to interpret the characters as a sequence of Parameter Value Language (PVL) syntax statements.

    This Parser implements the syntax of the PVL used by the Planetary Data System (PDS) as specified by the Consultative Committee for Space Data Systems in the Blue Book "Parameter Value Language Specification (CCSDS0006,8)", June 2000 [CCSDS 641.0-B-2] and Green Book "Parameter Value Language - A Tutorial", May 1992 [CCSDS 641.0-G-1] documents. PVL has been accepted by the International Standards Organization (ISO), as a Draft Standard (ISO/CD 14961:1997). The PVL syntax defines a Parameter with this basic format:

    [Comments]
    Name [= Value][;]

    The optional Comments are enclosed in C-style delimiters, or optionally preceeded by a crosshatch ('#') character on each line. The PVL syntax for a Value follows this format:

    [(|{]Datum [<Units>][, Datum [...]][)|} [<Units>]]

    The purpose of a Parser object is to assemble Parameter and Value objects using the PVL statements obtained from the associated String_Buffer_Reader.

    The class methods that perform the parsing of the character source are organized into a hierarchy:

    • Get
    • Add_To
      • Get_Parameter
        • Get_Comments
        • Get_Value
          • Get_Datum
            • Get_Quoted_String
          • Get_Units

    Higher level methods utilize lower level methods to assemble their constituent parts. At the top level an aggregate of all Parameters that can be interpreted from the input will be collected by getting as many Parameters as possible; a Parameter is produced from the input stream by getting any comments, a name String, and a Value; a Value includes as many datum and optional units descriptions that can be sequentially found in the input stream; and a datum is composed from primitive syntactic elements, including integer or real number representations or character strings which may be quoted (a Parameter name may also be a quoted string). Typically applications will only use the top method(s). Applications needing finer grained control over input stream parsing may, of course, use the lower level methods directly, however it is much easier to just get all of the Parameters from an input source and then manipulate the Parameter-Value object hierarchy.

    Each method that parses the input stream interprets the contents of the logical String_Buffer_Reader which presents the entire virtual contents of the stream from the current location onwards. Except for the top level which does not interpret the character source directly, these methods first seek forward from the current location to the beginning of a potentially relevant syntactic character sequence. If the sequence is recognized as suitable for the item the method is responsible for interpreting then the appropriate end of the sequence is found and the characters it contains are translated into the corresponding internal form of object class variable. If the translation is successful then the logical Next_Location of the String_Buffer_Reader is moved forward to the end of the sequence before the iterative interpretation of the stream continues. If, however, the beginning of a recognizable syntactic sequence is not found, or the translation of a sequence fails, then the method returns empty handed, and without having advanced the logical String_Buffer_Reader, to the invoking method which may invoke a different method in an attempt to get a different item or itself discontinue its efforts to assemble an item. Thus each parsing method either gets its item and advances the current location in the character stream, or does not get its item nor advance the stream; i.e. the PVL statements encountered in the character source are sequentially translated at the same time that the input stream is incrementally moved forward.

    @see String_Buffer_Reader @see Parameter @see Value @author Bradford Castalia, UA/PIRL @version 1.35 */ public class Parser extends String_Buffer_Reader { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Parser (1.35 2012/04/16 06:14:23)"; /*------------------------------------------------------------------------------ PVL syntax elements */ /** The default for enforcing {@link #Strict(boolean) strict} PVL syntax rules. */ public static boolean Strict_Default = false; private boolean Strict = Strict_Default; /** {@link #Verbatim_Strings(boolean) Verbatim strings} default. */ public static boolean Verbatim_Strings_Default = false; private boolean Verbatim_Strings = Verbatim_Strings_Default; /** {@link #String_Continuation(boolean) String continuation} default. */ public static boolean String_Continuation_Default = true; private boolean String_Continuation = String_Continuation_Default; /** The default for allowing {@link #Crosshatch_Comments(boolean) crosshatched-to-EOL comments}. */ public static boolean Crosshatch_Comments_Default = (! Strict_Default); private boolean Crosshatch_Comments = Crosshatch_Comments_Default; /** The default for treating {@link #All_Values_Strings(boolean) all values as strings}. */ public static boolean All_Values_Strings_Default = false; private boolean All_Values_Strings = All_Values_Strings_Default; /** Begins a "crosshatch comment" that extends to the end of the line: '#'. */ public static final char CROSSHATCH = '#'; /* The crosshatch comment start character must not be the same as the first character of COMMENT_START_DELIMITERS. */ /** The PVL character encoding: "US-ASCII". */ public static final String CHARACTER_ENCODING = "US-ASCII"; /** Characters reserved by the PVL syntax.

    {}()[]<>&\"',=;#%~|+! \t\r\n\f\013

    Some of these characters have special meanings in specific contexts as delimiters of PVL items. */ public static final String RESERVED_CHARACTERS = "{}()[]<>&\"',=;#%~|+! \t\r\n\f\013"; // Delimiter characters: /** Delimits a Parameter name from its Value: '='. */ public static final char PARAMETER_NAME_DELIMITER = '='; /** Delimits elements of an ARRAY Value: ','. */ public static final char PARAMETER_VALUE_DELIMITER = ','; /** Encloses a TEXT STRING Value: '"'. */ public static final char TEXT_DELIMITER = '"'; /** Encloses a SYMBOL STRING Value: '\''. */ public static final char SYMBOL_DELIMITER = '\''; /** Marks the start of a SET ARRAY Value: '{'. */ public static final char SET_START_DELIMITER = '{'; /** Marks the end of a SET ARRAY Value: '}'. */ public static final char SET_END_DELIMITER = '}'; /** Marks the start of a SEQUENCE ARRAY Value: '('. */ public static final char SEQUENCE_START_DELIMITER = '('; /** Marks the end of a SEQUENCE ARRAY Value: ')' */ public static final char SEQUENCE_END_DELIMITER = ')'; /** Marks the start of a Value units description: '<'. */ public static final char UNITS_START_DELIMITER = '<'; /** Marks the end of a Value units description: '>'. */ public static final char UNITS_END_DELIMITER = '>'; /** Encloses the datum of a Value in radix base notation: '#'. */ public static final char NUMBER_BASE_DELIMITER = '#'; /** Marks the end of a PVL statement: ';'. */ public static final char STATEMENT_END_DELIMITER = ';'; /** Indicates that the statement continues in the next record: '&'. */ public static final char STATEMENT_CONTINUATION_DELIMITER = '&'; /** Indicates that the quoted string continues unbroken in the next record: '-'. */ public static final char STRING_CONTINUATION_DELIMITER = '-'; // Delimiter strings: /** Character sequence that separates PVL statement lines: "\r\n" (CR-NL). */ public static final String LINE_BREAK = "\r\n"; /** Set of "whitespace" characters between PVL tokens: " \t\r\n\f\013" (SP, HT, CR, NL, FF, and VT). */ public static final String WHITESPACE = " \t\r\n\f\013"; /** Marks the start of a comment string: '/' and '*'. */ public static final String COMMENT_START_DELIMITERS = "/*"; /** Marks the end of a comment string: '*' and '/'. */ public static final String COMMENT_END_DELIMITERS = "*/"; /** Set of characters that suggests a DATE_TIME type of STRING Value: "-:". */ public static final String DATE_TIME_DELIMITERS = "-:"; /** Encloses a verbatim (uninterpreted) string: "\\v". */ public static final String VERBATIM_STRING_DELIMITERS = "\\v"; /* For convenience. Put WHITESPACE first to force the following characters to be cast as Strings for the concatenation. */ private static final String LINE_DELIMITERS = "\f\013" + LINE_BREAK, PARAMETER_NAME_DELIMITERS = WHITESPACE + PARAMETER_NAME_DELIMITER + STATEMENT_END_DELIMITER, PARAMETER_VALUE_DELIMITERS = WHITESPACE + PARAMETER_VALUE_DELIMITER + SET_START_DELIMITER + SET_END_DELIMITER + SEQUENCE_START_DELIMITER + SEQUENCE_END_DELIMITER + UNITS_START_DELIMITER + STATEMENT_END_DELIMITER; /* Each reserved parameter name is associated with a specific parameter classification in this list. */ private static final String[] SPECIAL_NAMES = { /* BEGIN_XXXX classifications are identical to XXXX classifications. Both forms must be in the list so they can be identified during parsing. However when translating a classification code to its special name (see Special_Name) the first entry will be returned. Since the BEGIN form is more "proper" they are put first in the list. */ "BEGIN_OBJECT", "OBJECT", "BEGINOBJECT", "END_OBJECT", "ENDOBJECT", "BEGIN_GROUP", "GROUP", "BEGINGROUP", "END_GROUP", "ENDGROUP", "END" }; private static final int[] SPECIAL_CLASSIFICATIONS = { Parameter.BEGIN_OBJECT, Parameter.OBJECT, Parameter.BEGIN_OBJECT, Parameter.END_OBJECT, Parameter.END_OBJECT, Parameter.BEGIN_GROUP, Parameter.GROUP, Parameter.BEGIN_GROUP, Parameter.END_GROUP, Parameter.END_GROUP, Parameter.END_PVL }; /** The default name of the aggregate Parameter to contain all Parameters when a Parser Get finds more than one Parameter: "The Container". */ public static final String CONTAINER_NAME = "The Container"; // When throwing an exception is inappropriate. private PVL_Exception First_Warning = null, Last_Warning = null; private boolean Use_First_Warning = true; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_AGGREGATE = 1 << 1, DEBUG_PARAMETER = 1 << 2, DEBUG_VALUE = 1 << 3, DEBUG_DATUM = 1 << 4, DEBUG_UNITS = 1 << 5, DEBUG_UTILITIES = 1 << 6, DEBUG_COMMENTS = 1 << 7, DEBUG_QUOTED_STRING = 1 << 8, DEBUG_WARNINGS = 1 << 9, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /*------------------------------------------------------------------------------ File input */ /** Creates a Parser using a Reader as the source of PVL statements, and sets a limit on the amount to read.

    @param reader The Reader to use as the source of characters. @param read_limit The maximum amount to read. @throws PVL_Exception From Set_Reader. @see #Set_Reader(Reader, long) */ public Parser ( Reader reader, long read_limit ) throws PVL_Exception {Set_Reader (reader, read_limit);} /** Creates a Parser using a Reader as the source of PVL statements, with no limit on the amount to read.

    @param reader The Reader to use as the source of characters. @throws PVL_Exception From Set_Reader. @see #Set_Reader(Reader, long) */ public Parser ( Reader reader ) throws PVL_Exception {Set_Reader (reader, NO_READ_LIMIT);} /** Creates a Parser using a File to create a new Reader as the source of PVL statements, and sets a limit on the amount to read.

    @param file The File to be the basis for a new Reader. @param read_limit The maximum amount to read. @throws PVL_Exception From Set_Reader. @see #Set_Reader(File, long) */ public Parser ( File file, long read_limit ) throws PVL_Exception {Set_Reader (file, read_limit);} /** Creates a Parser using a File to create a new Reader as the source of PVL statements, with no limit on the amount to read.

    @param file The File to be the basis for a new Reader. @throws PVL_Exception From Set_Reader. @see #Set_Reader(File, long) */ public Parser ( File file ) throws PVL_Exception {Set_Reader (file, NO_READ_LIMIT);} /** Creates a Parser using an InputStream to create a new Reader as the source of PVL statements, and sets a limit on the amount to read.

    @param input_stream The InputStream to be the basis for a new Reader. @param read_limit The maximum amount to read. @throws PVL_Exception From Set_Reader. @see #Set_Reader(InputStream, long) */ public Parser ( InputStream input_stream, long read_limit ) throws PVL_Exception {Set_Reader (input_stream, read_limit);} /** Creates a Parser using an InputStream to create a new Reader as the source of PVL statements, with no limit on the amount to read.

    @param input_stream The InputStream to be the basis for a new Reader. @throws PVL_Exception From Set_Reader. @see #Set_Reader(InputStream, long) */ public Parser ( InputStream input_stream ) throws PVL_Exception {Set_Reader (input_stream, NO_READ_LIMIT);} /** Creates a Parser with no source of PVL statements. */ public Parser () {} /*------------------------------------------------------------------------------ String input */ /** Creates a Parser using a String as the source of PVL statements.

    @param string The String to use as the source of characters. */ public Parser ( String string ) {super (string);} /** Creates a Parser using a character array as the source of PVL statements.

    @param char_array The source of characters. */ public Parser ( char[] char_array ) {this (String.valueOf (char_array));} /** Creates a Parser using a characer array subset as the source of PVL statements.

    @param char_array The source of characters. @param offset The array index where the character source starts. @param length The number of characters to source. */ public Parser ( char[] char_array, int offset, int length ) {this (String.valueOf (char_array, offset, length));} /*============================================================================== Accessors: */ /** Sets the Reader where the Parser will obtain characters.

    If {@link String_Buffer_Reader#Filter_Input() Filter_Input} is true, then a SIZED_RECORDS {@link #Warning() Warning} is registered.

    N.B.: When Filter_Input is used it will read the first two characters from the reader to test them for non-printable values, unless the test has already been done on the reader or {@link String_Buffer_Reader#Filter_Input(boolean) input filtering has been disabled}. If no characters are yet available from the reader - e.g. if the reader is backed by a pipe or network socket - then the read will block until two characters become available (or an IOException occurs). The base String_Buffer_Reader does its own Filter_Input test every time its internal buffer content is {@link String_Buffer_Reader#Extend() extended}. Therefore, to prevent the Filter_Input() test from blocking when the reader is set defer setting the reader until blocking is acceptable, it is known that the required input will be available or filtering has been disabled. For example:

    Parser parser = new Parser ();
    // The reader is certain not to need filtering.
    parser.Filter_Input (false);
    // Any of the Set_Reader methods may be used.
    parser.Set_Reader (reader, read_limit);
    

    @param reader The Reader source for Parser input. If null, then there is no PVL source and thus nothing to be parsed. @return This Parser. @throws PVL_Exception From Set_Reader. A FILE_IO form is thrown if there is an IOException from Filter_Input. @see String_Buffer_Reader#Set_Reader(Reader) @see String_Buffer_Reader#Filter_Input() @see String_Buffer_Reader#Read_Limit(long) */ public Parser Set_Reader ( Reader reader, long read_limit ) throws PVL_Exception { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("Parser.Set_Reader"); /* Set the Reader before the read limit: the read limit will not be changed if there is no Reader. */ Set_Reader (reader); Read_Limit (read_limit); if (reader != null) { try { // Possible Exception from the String_Buffer_Reader. if (Filter_Input ()) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("Parser.Set_Reader: Input_Is_Filtered"); Warning ( PVL_Exception.SIZED_RECORDS, "Input filtering will be applied.", 0 ); } } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "While testing for input filtering during Set_Reader.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } } return this; } /** Sets the Reader where the Parser will obtain characters by constructing an InputStreamReader from the specified InputStream and wrapping this in a BufferedReader, the same size as the Size_Increment of the String_Buffer_Reader, for efficiency. The Parser's {@link #CHARACTER_ENCODING CHARACTER_ENCODING} is used.

    @param input_stream The InputStream source for Parser input. If null then there is nothing to read. @return This Parser. @throws PVL_Exception From Set_Reader. A FILE_IO form is thrown if there is an UnsupportedEncodingException when constructing the InputStreamReader. @see String_Buffer_Reader#Size_Increment(int) */ public Parser Set_Reader ( InputStream input_stream, long read_limit ) throws PVL_Exception { if (input_stream == null) Set_Reader ((Reader)null, read_limit); else { try { Set_Reader ( new BufferedReader ( new InputStreamReader ( input_stream, CHARACTER_ENCODING ), Size_Increment () ), read_limit ); } catch (UnsupportedEncodingException exception) { throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Unable to create an InputStreamReader with the \"" + CHARACTER_ENCODING + "\" character encoding." + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } } return this; } /** Sets the Reader where the Parser will obtain characters by constructing a FileInputStream from the specified File and passing this to the {@link #Set_Reader(InputStream, long) Set_Reader (InputStream, long)} method.

    @param file The File source for Parser input. If null, there is nothing to read. @return This Parser. @throws PVL_Exception A FILE_IO error condition will occur if the File refers to a directory or a file for which read permission is not provided. Any other IOException that occurs while constructing the FileInputStream will also be converted into a PVL_Exception.FILE_IO exception. @see #Set_Reader(InputStream, long) */ public Parser Set_Reader ( File file, long read_limit ) throws PVL_Exception { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Parser.Set_Reader (File=" + file + ", read_limit=" + read_limit + ") <<<"); if (file == null) Set_Reader ((Reader)null, read_limit); else { if (file.isDirectory ()) throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "The pathname is a directory - " + file.getAbsolutePath () ); if (! file.canRead ()) throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Can't read the file - " + file.getAbsolutePath () ); // Create an InputStream from the File. try {Set_Reader (new FileInputStream (file), read_limit);} catch (Exception exception) { throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "Can't access the file - " + file.getAbsolutePath () + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } } return this; } /*------------------------------------------------------------------------------ Modes: */ /** Enables or disables strict PVL syntax rules in the Parser.

    Normally the Parser is tolerant. However, the default is controlled by the {@link #Strict_Default} value.

    N.B.: Enabling strict syntax rules will prevent treating {@link #All_Values_Strings(boolean) all Values as Strings}.

    @param strict true if strict rules are applied; false otherwise. @return This Parser. */ public Parser Strict ( boolean strict ) { Strict = strict; return this; } /** Tests if the Parser will enforce strict PVL syntax rules.

    @return true if the Parser will enforce strict syntax rules; false otherwise. */ public boolean Strict () {return Strict;} /** Enable or disable verbatim quoted strings.

    With format control (Verbatim_Strings disabled) multi-line quoted strings in PVL statements have white space surrounding the line breaks compressed to a single space character - except when String_Continuation is enabled and the last non-white space character on the line is a dash ("-"), in which case no space is included. This is because output formatting is expected to be controlled by embedded format characters which are processed by the Write method:

    \n - line break.
    \t - horizontal tab.
    \f - form feed (page break).
    \\ - backslash character.
    \v - verbatim (no formatting) till the next \v.

    Without format control (Verbatim_Strings enabled) all STRING Values are taken as-is.

    By default verbatim strings are disabled. However, the default is controlled by the {@link #Verbatim_Strings_Default} value.

    @param verbatim true if quoted strings in PVL statements are to be taken verbatim, without format control. @return This Parser. @see #Get_Parameter() @see #Get_Quoted_String() @see #String_Continuation(boolean) @see #Get_Comments() @see #Get_Datum(Value) @see #Get_Units() */ public Parser Verbatim_Strings ( boolean verbatim ) { Verbatim_Strings = verbatim; return this; } /** Tests if the Parser will handle quoted strings verbatim.

    @return true if quoted strings are taken verbatim; false otherwise. @see #Verbatim_Strings(boolean) */ public boolean Verbatim_Strings () {return Verbatim_Strings;} /** Enable or disable string continuation.

    When string continuation is enabled (the default) and {@link #Verbatim_Strings(boolean) verbatim strings} is disabled (the default) the occurrance in a quoted string of a {@link #STRING_CONTINUATION_DELIMITER} as the last character before the new line sequence causes the string continuation delimiter and all characters up to the next non-whitespace character to be removed from the string; i.e. the string continues on the next line after any whitespace.

    By default string continuation is enabled. However, the default is controlled by the {@link #String_Continuation_Default} value.

    @param continuation true if string continuation is to be enabled; false otherwise. @return This Parser. @see #Get_Quoted_String() @see #Verbatim_Strings(boolean) */ public Parser String_Continuation ( boolean continuation ) { String_Continuation = continuation; return this; } /** Tests if the Parser will recognize multi-line string continuation.

    @return true if quoted string conintuation will be recognized; false otherwise. @see #String_Continuation(boolean) */ public boolean String_Continuation () {return String_Continuation;} /** Enable or disable recognition of crosshatch comments. A Crosshatch comment begins with the {@link #CROSSHATCH CROSSHATCH} character and extends to the end of the current line (marked by a {@link #LINE_BREAK LINE_BREAK} character). Crosshatch comments are never recognized in {@link #Strict(boolean) Strict} mode.

    Note: Crosshatch comments are not recognized in the PVL syntax specification. Because of their common use in configuration files, this special extension is provided to accommodate such applications. Be default crosshatch comments are not recognized.

    By default crosshatch comments are enabled. However, the default is controlled by the {@link #Crosshatch_Comments_Default} value.

    @param crosshatch_comments true if crosshatch comments are to be recognized; false otherwise. @return This Parser. */ public Parser Crosshatch_Comments ( boolean crosshatch_comments ) { Crosshatch_Comments = crosshatch_comments; return this; } /** Tests if crosshatch comments will be recognized.

    Regardless of this test, crosshatch comments are never recognized in {@link #Strict(boolean) Strict} mode.

    @return true if crosshatch comments will be recognized; false otherwise. @see #Crosshatch_Comments(boolean) */ public boolean Crosshatch_Comments () {return Crosshatch_Comments;} /** Enable or disable treating all values as strings.

    When enabled, and {@link #Strict(boolean) strict} syntax rules are not enabled, all PVL parameter values will be parsed as {@link Value#STRING Strings}; no other Value {@link Value#Type() Type} will be generated. This may be useful in cases where, for example, a {@link Value#NUMERIC numeric} Type Value would be generated from a PVL value representation that can not be converted to a binary value with sufficient precision, or where unquoted PVL value representations that would otherwise be expected to be {@link Value#IDENTIFIER identifiers} happen to contain all numeric digit characters.

    By default treating all values as strings is disabled. However the default is controlled by the {@link #All_Values_Strings_Default} value.

    @param all_values_strings true if all Values are to be generated as {@link Value#STRING} Types; false otherwise. @return This Parser. */ public Parser All_Values_Strings ( boolean all_values_strings ) { All_Values_Strings = all_values_strings; return this; } /** Tests if the Parser will treat all values as strings.

    Regardless of this test, treating all values as strings will not be enforced if {@link #Strict(boolean) Strict} mode is enabled.

    @return true if the Parser will treat all values as strings; false otherwise. @see #All_Values_Strings(boolean) */ public boolean All_Values_Strings () {return All_Values_Strings;} /** Gets the current warning status.

    When conditions are encountered that are unusual enough to warrant attention, but not an error condition that would prevent successful processing which would cause an exception to be thrown, a warning condition is registered. The warning is in the form of a PVL_Exception that was not thrown. The current warning status is either the {@link #First_Warning(boolean) First_Warning} or the {@link #Last_Warning(boolean) Last_Warning} since a {@link #Reset_Warning() Reset_Warning}.

    @return The current warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #First_Warning(boolean) @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Warning () { if (Use_First_Warning) return First_Warning; return Last_Warning; } /** Clears any warning status so that the Warning method will return null until the next warning condition occurs. @return This Parser. @see #Warning() */ public Parser Reset_Warning () { First_Warning = null; Last_Warning = null; return this; } /** Enables or disables returning the first warning that occurs as the current warning status. The first warning is one that occurs when the current warning status is null.

    @param first true to enable returning the first warning status; false to return the last warning that occurred as the current warning status. @return This Parser. @see #Warning() @see #First_Warning() @see #Reset_Warning() */ public Parser First_Warning ( boolean first ) { Use_First_Warning = first; return this; } /** Returns the first warning since the last Reset_Warning.

    @return The first warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #First_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception First_Warning () {return First_Warning;} /** Enables or disables returning the last warning that occurs as the current warning status. The last warning is the most recent one regarless of any previous warning conditions that may have occured without an intervening Reset_Warning.

    @param last true to enable returning the last warning status; false to return the first warning condition that occurred as the current warning status. @return This Parser. @see #Warning() @see #Last_Warning() @see #Reset_Warning() */ public Parser Last_Warning ( boolean last ) { Use_First_Warning = ! last; return this; } /** Returns the last warning since a Reset_Warning.

    @return The last warning status as a PVL_Exception object, or null if no warning condition is registered. @see PVL_Exception @see #Warning() @see #Last_Warning(boolean) @see #Reset_Warning() */ public PVL_Exception Last_Warning () {return Last_Warning;} /*============================================================================== PVL parser: */ /*------------------------------------------------------------------------------ Parameter */ /** Gets as many Parameters as possible.

    When the source of PVL statements is a Reader, then an aggregate Parameter named {@link #CONTAINER_NAME CONTAINER_NAME} will be provided to contain all the Parameters found (zero or more). If this Parser was created with a String as the source of PVL statements, then a container Parameter will only be provided if more than one Paramter is found: the single Parameter found, or an empty UNKNOWN Parameter if nothing is found.

    @return A Parameter containing everything found from the input source. @throws PVL_Exception If an unrecoverable problem occurred while parsing the input source. @see #Add_To(Parameter) @see String_Buffer_Reader#String_Source() */ public Parameter Get () throws PVL_Exception { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println (">>> Parser Get"); Parameter The_Parameter = new Parameter (CONTAINER_NAME); Add_To (The_Parameter); if (String_Source ()) { /* Parameters read from a string only need a new aggregate to contain them when the string contains multiple, ungrouped parameters to be returned as a group. Otherwise the single parameter in the string representation is itself returned. If the string does not contain any recognizable parameters an unnamed UNKNOWN parameter is returned. Parameters read from a file are guaranteed to be returned in a new aggregate named CONTAINER_NAME. */ if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("Parser Get: String_Reader"); if (The_Parameter.Is_Empty ()) { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("Parser Get: Empty"); The_Parameter.Name ("").set_classification (Parameter.UNKNOWN); } else if (The_Parameter.List_Size () == 1) { // Just return the single parameter. if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("Parser Get: One parameter"); The_Parameter = The_Parameter.Remove (0); } } if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("<<< Parser Get"); return The_Parameter; } /*------------------------------------------------------------------------------ */ /** Adds to a Parameter all Parameters found from the input source. Any Paramter added that is itself an AGGREGATE classification will recursively invoke this method on the new aggregate Parameter.

    While the source of PVL statements is not empty and {@link #Get_Parameter() Get_Parameter} can assemble a Parameter from the source, each new Parameter is {@link Parameter#Add(Parameter) Add}ed to the specified Parameter's aggregate list. Note: Parameters having END classifications are not added, instead they stop the addition of Parameters to the current aggregate list; additions will continue with the parent of the current aggregate on returning from recursive method invocations. However, if an END_PVL Parameter is encountered no more Parameters will be added regardless of the recursion level.

    @param The_Aggregate The aggregate Parameter to which to add new Parameters from the input source. @return The_Aggregate Parameter. @throws PVL_Exception

    BAD_ARGUMENT
    The_Aggregate Parameter is actually an ASSIGNMENT with a non-null Value.
    AGGREGATE_CLOSURE_MISMATCH
    In Strict mode, when an END_AGGREGATE Parameter does not match the specific classification of the Parameter containing the aggregate list. This is only a Warning when Strict mode is not enabled.
    Others are possible during parsing.
    @see Parameter#AGGREGATE @see String_Buffer_Reader#Is_Empty() @see #Get_Parameter() @see Parameter#Add(Parameter) @see Parameter#END @see Parameter#END_AGGREGATE @see Parameter#END_PVL */ public Parameter Add_To ( Parameter The_Aggregate ) throws PVL_Exception { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println (">>> Parser Add_To"); add_to (The_Aggregate); if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("<<< Parser Add_To"); return The_Aggregate; } private boolean add_to ( Parameter The_Aggregate ) throws PVL_Exception { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println (">>> add_to: " + The_Aggregate.Name () + " Next_Location = " + Next_Location () + '\n' + The_Aggregate.Description ()); if (Is_Empty ()) { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: Is_Empty\n" +"<<< add_to"); return false; } // Ensure the correct parameter classification. if (The_Aggregate.Is_Assignment () && The_Aggregate.Value () != null) throw new PVL_Exception ( ID, PVL_Exception.BAD_ARGUMENT, "Can't add to the ASSIGNMENT parameter \"" + The_Aggregate.Name () + "\"", Next_Location () ); if (! The_Aggregate.Is_Aggregate ()) The_Aggregate.set_classification (Parameter.GROUP); // Collect all the parameters: boolean continue_parsing = true; while (! Is_Empty ()) { // Get the next parameter. Parameter The_Parameter; if ((The_Parameter = Get_Parameter ()) == null) { // Failed to get a parameter. continue_parsing = false; // Stop parsing. break; } if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: Got " + The_Parameter.Classification_Name () + " named " + The_Parameter.Name ()); /*-------------------------------------------------------------------------- Check for END parameters: */ if (The_Parameter.Is_End ()) { if (The_Parameter.Is_End_Aggregate ()) { if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: Is_End_Aggregate"); if (The_Parameter.Classification () != (The_Aggregate.Classification () | Parameter.END)) { /* The begin aggregate parameter doesn't match the end aggregate parameter. */ Warning ( PVL_Exception.AGGREGATE_CLOSURE_MISMATCH, "For the " + The_Aggregate.Classification_Name () + " aggregate named \"" + The_Aggregate.Name () + "\"\n" + " with an " + The_Parameter.Classification_Name () + " parameter.", Next_Location () ); if (Strict) throw Last_Warning; } } if (The_Parameter.Is_End_PVL ()) { // END input here. if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: Is_End_PVL"); continue_parsing = false; } break; } // Add the parameter to the aggregate's list. if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: Add to " + The_Aggregate.Name () + " -\n" + The_Parameter.Description ()); The_Aggregate.Add (The_Parameter); if (The_Parameter.Is_Aggregate () && // Recursively add the parameters for this aggregate parameter. ! (continue_parsing = add_to (The_Parameter))) { // Reached the end of input. if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: End of input -\n" + The_Aggregate.Description ()); break; } } if ((DEBUG & DEBUG_AGGREGATE) != 0) System.out.println ("add_to: The_Aggregate -\n" + The_Aggregate.Description () + "\n" +" continue_parsing - " + continue_parsing + '\n' +"<<< add_to: Next_Location = " + Next_Location ()); return continue_parsing; } /*------------------------------------------------------------------------------ */ /** Gets a Parameter from the source of PVL statements.

    First, {@link #Get_Comments Get_Comments} is used to to collect any and all comment sequences preceeding the parameter proper as the Parameter's Comments.

    The next sequence of non-{@link #WHITESPACE WHITESPACE} characters is taken to be the Parameter's Name. If the sequence is quoted (i.e. starts with a {@link #TEXT_DELIMITER TEXT_DELIMITER} or {@link #SYMBOL_DELIMITER SYMBOL_DELIMITER}), then the name is all characters within the quotes. Otherwise it is all characters up to but not including the next {@link #PARAMETER_NAME_DELIMITER PARAMETER_NAME_DELIMITER}, {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER}, whitespace character, or comment sequence. The name is checked for any {@link #Bad_Character(String) Bad_Character} and a Warning will be registered (exception thrown in Strict mode) if one is found. If Verbatim_Strings is not enabled, then all character escape sequences in the name are replaced with their corresponding special characters.

    If the name is associated with a {@link #Special_Classification(String) Special_Classification}, then the Parameter is given that classification; otherwise it is tentatively classified as a TOKEN.

    If, after any whitespace or comments following the Parameter name, a {@link #PARAMETER_NAME_DELIMITER PARAMETER_NAME_DELIMITER} is found, then the {@link #Get_Value() Get_Value} method is used to obtain the expected Parameter Value. If the Parameter had been given the TOKEN classification, then the classification is upgraded to ASSIGNMENT. If, however, the Parameter had been given an AGGREGATE classification as a result of its special name, and the Value obtained is a STRING type, then the Parameter's name is changed to the Value String; if, in this case, the Value found is not a STRING type a Warning will be registered (exception thrown in Strict mode).

    Having assembled a valid Parameter, the Next_Location in the input stream is moved forward past any whitespace or STATEMENT_END_DELIMITER.

    @return The next Parameter assembled from the input stream, or null if no Parameter can be assembled because the input stream is empty. @throws PVL_Exception

    ILLEGAL_SYNTAX
    Strict mode is enabled and the Parameter name was quoted.
    RESERVED_CHARACTER
    The Parameter name contained a bad character and Strict mode is enabled.
    GROUP_VALUE
    The Value obtained for an AGGREGATE Parameter is not a STRING type.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see #Get_Comments() @see Parameter#Comments() @see Parameter#Name() @see #translate_from_escape_sequences(String_Buffer) @see #Special_Classification(String) @see #Get_Value() */ public Parameter Get_Parameter () throws PVL_Exception { if (Is_Empty ()) return null; if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println (">>> Get_Parameter: Next_Location = " + Next_Location ()); Parameter The_Parameter = new Parameter (); long delimiter, location; int index; /*.............................................................................. Collect any leading comments before the parameter name: */ The_Parameter.Comments (Get_Comments ()); try { // Possible IOException from the String_Buffer_Reader. // Ignore any statement end delimiter. if (Is_End (delimiter = Next_Location (Skip_Over (Next_Location (), WHITESPACE + STATEMENT_END_DELIMITER)))) return null; /*.............................................................................. Get the parameter name: */ if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Getting the parameter name starting at " + delimiter); if (Char_At (delimiter) == TEXT_DELIMITER || Char_At (delimiter) == SYMBOL_DELIMITER) { // It's a quoted string. Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Parameter names should not be quoted.", delimiter ); if (Strict) throw Last_Warning; The_Parameter.Name (Get_Quoted_String ()); } else { // Find the parameter name's trailing delimiter. if ((delimiter = Skip_Until (Next_Location (), PARAMETER_NAME_DELIMITERS)) < 0) delimiter = End_Location (); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Getting the parameter name ending at " + delimiter); // Get the parameter name substring. The_Parameter.Name (Substring (Next_Location (), delimiter)); if ((index = The_Parameter.Name ().indexOf (COMMENT_START_DELIMITERS)) > 0) { // Only take the part up to the trailing comment. The_Parameter.Name (The_Parameter.Name ().substring (0, index)); delimiter = Next_Location () + index; } // Check for reserved characters in the parameter name. if ((index = Bad_Character (The_Parameter.Name ())) >= 0) { Warning ( PVL_Exception.RESERVED_CHARACTER, "At character " + index + " of the parameter name \"" + The_Parameter.Name () + "\"", delimiter + index ); if (Strict) throw Last_Warning; } Next_Location (delimiter); } if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Name = " + The_Parameter.Name ()); if (! Verbatim_Strings) // Translate escape sequences to special characters. The_Parameter.Name (translate_from_escape_sequences (new String_Buffer (The_Parameter.Name ())).toString ()); /*.............................................................................. Set the initial parameter classification: */ // Check for special names for special classifications. int classification; if ((classification = Special_Classification (The_Parameter.Name ())) > 0) The_Parameter.set_classification (classification); else The_Parameter.set_classification (Parameter.TOKEN); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Tentative classification = " + The_Parameter.Classification_Name ()); /*.............................................................................. Get the parameter value(s): */ /* Find the parameter name delimiter character that separates the parameter name from the parameter values list. */ if (Is_End (delimiter = Next_Location (skip_whitespace_and_comments (Next_Location ())))) { if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("<<< Get_Parameter: End at " + delimiter); return The_Parameter; } if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Expecting '" + PARAMETER_NAME_DELIMITER + "', found '" + Char_At (delimiter) + "'"); if (Char_At (delimiter) == PARAMETER_NAME_DELIMITER) { // This is where the values string starts. if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: PARAMETER_NAME_DELIMITER at " + delimiter); Next_Location (++delimiter); /* Elevate the parameter classification to assignment unless it's already been identified with one of the special classifications. */ if (The_Parameter.Is_Token ()) The_Parameter.set_classification (Parameter.ASSIGNMENT); if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Getting the parameter values."); Value value = Get_Value (); if (value != null) { if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("Get_Parameter: Got a value."); if (The_Parameter.Is_Aggregate ()) { // Set the parameter name to the string value. The_Parameter.Name (value.String_Data ()); if (! value.Is_String ()) { // The value is inappropriate for an aggregate. Warning ( PVL_Exception.GROUP_VALUE, "Non-STRING value for the " + The_Parameter.Classification_Name () + " parameter.", delimiter ); if (Strict) throw Last_Warning; } } else The_Parameter.set_data (value); } } // Skip any trailing white space and statement end delimiters. Next_Location (Skip_Over (Next_Location (), WHITESPACE + STATEMENT_END_DELIMITER)); } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Parameter.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } if ((DEBUG & DEBUG_PARAMETER) != 0) System.out.println ("<<< Get_Parameter: Next_Location = " + Next_Location ()); return The_Parameter; } /*------------------------------------------------------------------------------ */ /** Gets the next sequence of comments from the source of PVL statements as a single comment String. After skipping over any whitespace, the next characters must start a comment sequence or nothing (null) is returned.

    A PVL comment uses C-style conventions: It starts after the {@link #COMMENT_START_DELIMITERS COMMENT_START_DELIMITERS} and ends before the {@link #COMMENT_END_DELIMITERS COMMENT_END_DELIMITERS}. A comment without the closing COMMENT_END_DELIMITERS will result in a MISSING_COMMENT_END exception in Strict mode; otherwise a Warning is registered and the next line break, {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER}, or the end of the input stream is taken as the end of the comment. Note: Though an effort is made to recover from encountering an unending comment, this will only be effective when no other normally closed comment occurs in the input stream (if a normally closed comment does occur after an unclosed comment, the latter will be taken as the end of the former), and in this case the input stream will have been read into memory until it is empty.

    Sequential comments, with nothing but white space intervening, are accumulated with a single new-line ('\n') chararacter separating them in the resulting String that is returned. In Strict mode comments that wrap across line breaks cause an exception. When Verbatim_Strings are not enabled whitespace is trimmed from the end of each comment (but not the beginning), and escape sequences are translated into their corresponding special characters.

    If any comments are found the Next_Location of the input stream is moved to the position immediately following the last comment.

    @return A String containing the sequence of comments found, or null if no comment occurs before the next PVL item or the end of input. @throws PVL_Exception

    ILLEGAL_SYNTAX
    Strict mode is enabled and a comment continues on more than one line.
    MISSING_COMMENT_END
    A comment does not have COMMENT_END_DELIMITERS and Strict mode is enabled. If Strict is not enabled and a line or statement delimiter can not be found, then the exception is thrown.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see #Verbatim_Strings(boolean) */ public String Get_Comments () throws PVL_Exception { if (Is_Empty ()) return null; String_Buffer comments = null; long location = Next_Location (), comment_start, comment_end; if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println (">>> Get_Comments: Next_Location = " + location); try { // Possible IOException from the String_Buffer_Reader. // Accumulate all sequential comments. while (true) { // Find the beginning of the comment string. if (Is_End (location = skip_crosshatch_comments (location)) || ! Equals (location, COMMENT_START_DELIMITERS)) break; comment_start = location + COMMENT_START_DELIMITERS.length (); if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println ("Get_Comments: comment starts at " + comment_start); // Find the end of the comment string. if ((comment_end = Location_Of (location, COMMENT_END_DELIMITERS)) < 0) { // No comment end. Warning ( PVL_Exception.MISSING_COMMENT_END, "For comment starting with \"" + Substring (comment_start, Math.min (comment_start + 20, End_Location ())) + "\" ...", location ); if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println ("Get_Comments:\n" + Last_Warning.getMessage ()); if (Strict) throw Last_Warning; // Assume it ends at the end of the line. if ((comment_end = Skip_Until (location, LINE_DELIMITERS + STATEMENT_END_DELIMITER)) < 0) location = End_Location (); location = Skip_Over (comment_end, LINE_DELIMITERS + STATEMENT_END_DELIMITER); } else location = comment_end + COMMENT_END_DELIMITERS.length (); String_Buffer comment = new String_Buffer (Substring (comment_start, comment_end)); if (Strict && comment.skip_until (0, LINE_BREAK) >= 0) throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Multi-line comments are not allowed in Strict mode.\n" + "For comment starting with \"" + Substring (comment_start, Math.min (comment_start + 20, End_Location ())) + "\" ...", comment_start ); // Clean up the comment String. if (! Verbatim_Strings) translate_from_escape_sequences (comment.trim_end ()); // Leave whitespace at the beginning. // Append the comment to the comments. if (comments == null) comments = comment; else comments.append ("\n" + comment.toString ()); if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println ("Get_Comments: length " + comments.length () + " - " + comments.toString ()); } Next_Location (location); } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Comments.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } if ((DEBUG & DEBUG_COMMENTS) != 0) System.out.println ("<<< Get_Comments: Next_Location = " + Next_Location ()); return (comments == null) ? null : comments.toString (); } /*------------------------------------------------------------------------------ Value */ /** Gets a Value from the source of PVL statements.

    If the next character, after skipping any whitespace and comments, is a {@link #SET_START_DELIMITER SET_START_DELIMITER} or {@link #SEQUENCE_START_DELIMITER SEQUENCE_START_DELIMITER} the Value being assembled is typed as a SET or SEQUENCE, respectively, and the Next_Location in the input stream is moved over the character; otherwise the Value is tentatively typed as a SET, and the Next_Location is not moved.

    Now a cycle is entered to obtain as many sequential datum values as are available. The first step is to skip any whitespace and comments and test the character that is found. If the character is a {@link #SET_END_DELIMITER SET_END_DELIMITER} or {@link #SEQUENCE_END_DELIMITER SEQUENCE_END_DELIMITER}, then the Next_Location in the input stream is moved over the character and Get_Units is used to set the ARRAY Value's units description before ending the datum cycle. If the character is a {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER} the datum cycle ends. A reserved {@link #PARAMETER_NAME_DELIMITER PARAMETER_NAME_DELIMITER}, {@link #PARAMETER_VALUE_DELIMITER PARAMETER_VALUE_DELIMITER}, {@link #UNITS_START_DELIMITER UNITS_START_DELIMITER}, {@link #UNITS_END_DELIMITER UNITS_END_DELIMITER}, or {@link #NUMBER_BASE_DELIMITER NUMBER_BASE_DELIMITER} character here is an ILLEGAL_SYNTAX exception. For any other character the Next_Location is moved forward to its position as a possible datum.

    When the character at this location is a {@link #SET_START_DELIMITER SET_START_DELIMITER} or {@link #SEQUENCE_START_DELIMITER SEQUENCE_START_DELIMITER} this method is called recursively to get a subarray as the datum. Otherwise the Get_Datum method is used to get a basic value followed by the Get_Units method to get any units description for the new datum. If no datum is obtained the datum cycle ends, otherwise the new datum is added to the Vector of Value's being accumulated for this new Value.

    After skipping any whitespace and comments the next character is checked. A reserved {@link #PARAMETER_NAME_DELIMITER PARAMETER_NAME_DELIMITER}, {@link #UNITS_START_DELIMITER UNITS_START_DELIMITER}, {@link #UNITS_END_DELIMITER UNITS_END_DELIMITER}, or {@link #NUMBER_BASE_DELIMITER NUMBER_BASE_DELIMITER} character here is an ILLEGAL_SYNTAX exception. A {@link #SET_START_DELIMITER SET_START_DELIMITER} or {@link #SEQUENCE_START_DELIMITER SEQUENCE_START_DELIMITER} character will also generate an ILLEGAL_SYNTAX exception, but if Strict mode is not enabled this will just be registered as a Warning and the Next_Location will be moved to the character's position before continuing the datum cycle from the beginning. If the character is a {@link #PARAMETER_VALUE_DELIMITER PARAMETER_VALUE_DELIMITER} the Next_Location is moved over the character and the datum cycle returns to the beginning. For a {@link #SET_END_DELIMITER SET_END_DELIMITER} or {@link #SEQUENCE_END_DELIMITER SEQUENCE_END_DELIMITER}, if the character does not correspond to the delimiter that began the array an ARRAY_CLOSURE_MISMATCH Warning is registered (this exception is thrown in Strict mode). Then the Next_Location is moved over the character and the Get_Units method is used to set this Value's units description before ending the datum cycle. Any other character also causes the datum cycle to end.

    After the datum cycle has collected as many Values as possible, if this new Value was not begun with a {@link #SET_START_DELIMITER SET_START_DELIMITER} or {@link #SEQUENCE_START_DELIMITER SEQUENCE_START_DELIMITER} and the accumulated Values Vector containins less than two Values then the initial tentative SET type does not apply. In this case an empty accumlated Values Vector results in this new Value being an UNNOWN type (i.e. it is empty). When only one Value was collected it is the new Value that is returned. When two or more Values were collected the Vector containing them is set as the data of this ARRAY Value.

    @return The next Value assembled from the input stream, or null if no Value can be assembled because the input stream is empty. @throws PVL_Exception

    ILLEGAL_SYNTAX
    A misplaced reserved character was found.
    ARRAY_CLOSURE_MISMATCH
    The delimiter character ending an array of Values does not correspond to the one that began the array.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see Value#set_type(int) @see Value#Units(String) @see #Get_Datum(Value) */ public Value Get_Value () throws PVL_Exception { if (Is_Empty ()) return null; if ((DEBUG & DEBUG_VALUE) != 0) System.out.println (">>> Get_Value: Next_Location = "+ Next_Location ()); Value // The Value being built. The_Value = new Value (); Vector // The data array for this value. The_Array = new Vector (); Value // Each datum added to the array. datum; long delimiter, location, array_start_location; int start_type = 0, end_type = 0; try { // Possible IOException from the String_Buffer_Reader. if (Is_End (delimiter = Next_Location (skip_whitespace_and_comments (Next_Location ())))) { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: " + delimiter + " Is_End\n<<< Get_Value: Next_Loction = " + delimiter); return null; } /*.............................................................................. Check for an initial array start character. */ array_start_location = delimiter; if (Char_At (delimiter) == SET_START_DELIMITER) { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Set detected."); The_Value.set_type (start_type = Value.SET); } else if (Char_At (delimiter) == SEQUENCE_START_DELIMITER) { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Sequence detected."); The_Value.set_type (start_type = Value.SEQUENCE); } if (start_type != 0) Next_Location (++delimiter); else { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Initial default is set."); The_Value.set_type (Value.SET); // Tentative default. } /*.............................................................................. Get as many values as possible. */ Get_Values: while (true) { // Find the values string. if (Is_End (delimiter = skip_whitespace_and_comments (Next_Location ()))) break; // Check for a valid values string: switch (Char_At (delimiter)) { /* End of array cases: Note: An empty array is valid. */ case SET_END_DELIMITER: end_type = Value.SET; case SEQUENCE_END_DELIMITER: if (end_type == 0) end_type = Value.SEQUENCE; if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: End of array at location " + delimiter); Next_Location (++delimiter); // Get any units string for this array. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Getting any units."); The_Value.Units (Get_Units ()); case STATEMENT_END_DELIMITER: break Get_Values; // Syntax error cases: case PARAMETER_NAME_DELIMITER: case PARAMETER_VALUE_DELIMITER: case UNITS_START_DELIMITER: case UNITS_END_DELIMITER: case NUMBER_BASE_DELIMITER: /* Note: It would be possible here to be VERY tolerant and just take these illegal characters as the first character of an identifier string, but this seems unlikely in any case. */ throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Expected a value, but found '" + Char_At (delimiter) + "'", delimiter ); // Possible value: default: Next_Location (delimiter); break; } // Get the value: if (Char_At (delimiter) == SET_START_DELIMITER || Char_At (delimiter) == SEQUENCE_START_DELIMITER) { // The value is an array of values. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Starting a new array."); if ((datum = Get_Value ()) == null) break Get_Values; } else { // Get the datum. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Getting a datum."); if ((datum = Get_Datum (new Value ())) == null) break Get_Values; // Get any units string for this datum. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Getting any units."); datum.Units (Get_Units ()); } // Add the value to the array. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Adding the datum to the value array."); The_Array.add (datum); /* Find the next word. Note: Leave the Next_Location before any comments in case they occur as lead-in to the next PVL statement (i.e. this may be the end of the current statement). Update the Next_Location when the new location is recognized as value syntax. */ if (Is_End (location = skip_whitespace_and_comments (Next_Location ()))) break Get_Values; // Check what comes next: switch (Char_At (location)) { // Another datum is expected: case PARAMETER_VALUE_DELIMITER: Next_Location (++location); break; case SET_START_DELIMITER: case SEQUENCE_START_DELIMITER: Warning ( PVL_Exception.ILLEGAL_SYNTAX, "Expected another datum, but found '" + Char_At (location) + "'", location ); if (Strict) throw Last_Warning; Next_Location (location); break; // End of array cases: case SET_END_DELIMITER: end_type = Value.SET; case SEQUENCE_END_DELIMITER: if (end_type == 0) end_type = Value.SEQUENCE; if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: End of array at location " + location); if (start_type != end_type) { Warning ( PVL_Exception.ARRAY_CLOSURE_MISMATCH, "For Value array starting at location " + array_start_location + " with \"" + Substring (array_start_location, Math.min (array_start_location + 20, End_Location ())) + "\" ...", location ); if (Strict) throw Last_Warning; } Next_Location (++location); // Get any units string for this array. if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Getting any units."); The_Value.Units (Get_Units ()); break Get_Values; // Syntax error cases: case PARAMETER_NAME_DELIMITER: case UNITS_START_DELIMITER: case UNITS_END_DELIMITER: case NUMBER_BASE_DELIMITER: throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "Expected another datum, but found '" + Char_At (location) + "'", location ); default: // Not a recognized value syntax. break Get_Values; } } } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Value.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } /*.............................................................................. Check the results: */ if (start_type == 0 && end_type == 0 && The_Array.size () <= 1) { if (The_Array.size () == 0) // Didn't get anything. The_Value.set_type (Value.UNKNOWN); else { /* The_Array contains a single, non-array (parsed from an undecorated string) value. Make this The_Value's datum. Note that this case can only occur at the root level for a PVL statement with a single non-array value. */ if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("Get_Value: Single value returned."); The_Value = (Value)The_Array.firstElement (); } } else The_Value.set_data (The_Array); if ((DEBUG & DEBUG_VALUE) != 0) System.out.println ("<<< Get_Value: Next_Location = " + Next_Location ()); return The_Value; } /*------------------------------------------------------------------------------ */ /** Gets a datum from the source of PVL statements.

    After skipping any whitespace or comments, the next character is checked to determine the type of datum to parse. For a {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER} nothing happens. A reserved {@link #PARAMETER_NAME_DELIMITER PARAMETER_NAME_DELIMITER}, {@link #PARAMETER_VALUE_DELIMITER PARAMETER_VALUE_DELIMITER}, {@link #SET_START_DELIMITER SET_START_DELIMITER}, {@link #SET_END_DELIMITER SET_END_DELIMITER}, {@link #SEQUENCE_START_DELIMITER SEQUENCE_START_DELIMITER}, {@link #SEQUENCE_END_DELIMITER SEQUENCE_END_DELIMITER}, {@link #UNITS_START_DELIMITER UNITS_START_DELIMITER}, {@link #UNITS_END_DELIMITER UNITS_END_DELIMITER}, or {@link #NUMBER_BASE_DELIMITER NUMBER_BASE_DELIMITER} character here is an ILLEGAL_SYNTAX exception. For a {@link #TEXT_DELIMITER TEXT_DELIMITER} or {@link #SYMBOL_DELIMITER SYMBOL_DELIMITER} the Get_Quoted_String method is used to set the datum of the Value and its type is set to TEXT or SYMBOL respectively.

    For any ordinary character the substring up to, but not including, the next {@link #WHITESPACE WHITESPACE}, {@link #PARAMETER_VALUE_DELIMITER PARAMETER_VALUE_DELIMITER}, {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER}, {@link #COMMENT_START_DELIMITERS COMMENT_START_DELIMITERS}, any of the SET/SEQUENCE/UNITS START/END delimiters, or the end of the input stream is used for parsing a datum. If Verbatim_Strings is not enabled, then all escape sequences in the substring are converted to their special character equivalents.

    The datum substring is first assumed to represent a number. If the substring contains a {@link #NUMBER_BASE_DELIMITER NUMBER_BASE_DELIMITER} ('#') the number is presumed to be in radix base notation:

    [sign]base#value#

    In this case the initial base integer is obtained using the Integer.parseInt method and becomes the Value's Base, and the value number is obtained using the Long.parseLong method with the base argument specified and becomes the Value's datum. The sign is applied to the value number. The Value becomes type INTEGER. Without the NUMBER_BASE_DELIMITER the datum substring is taken to be in decimal notation. If this number conversion fails, then the Double.valueOf method is tried on the datum substring to produce a type REAL Value.

    N.B.: If treating {@link #All_Values_Strings(boolean) all values as strings} has been enabled, and {@link #Strict(boolean) Strict} mode is not enabled, then the value is not assumed to be a number; it is always taken as string.

    If parsing the datum substring as a number fails, then the Value is a STRING and its datum is the substring. If the substring contains one of the {@link #DATE_TIME_DELIMITERS DATE_TIME_DELIMITERS} the Value is given the DATE_TIME type. Otherwise it is the IDENTIFIER type. The datum substring is also checked for a Bad_Character with a Warning being registered (exception thrown in Strict mode) if one is found.

    Once a datum has been given to the Value the Next_Location in the input stream is moved to the position immediately following the datum substring.

    @param The_Value The Value to which the next datum is to be applied. @return The_Value, or null if the input stream is empty. @throws PVL_Exception

    ILLEGAL_SYNTAX
    A misplaced reserved character was found.
    RESERVED_CHARACTER
    A STRING Value contains a reserved character.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see Integer#parseInt(String) @see Long#parseLong(String, int) @see Double#valueOf(String) @see Value#set_data(Object) @see Value#Base(int) @see Value#set_type(int) @see #translate_from_escape_sequences(String_Buffer) */ public Value Get_Datum ( Value The_Value ) throws PVL_Exception { if (Is_Empty ()) return null; if ((DEBUG & DEBUG_DATUM) != 0) System.out.println (">>> Get_Datum: Next_Location = " + Next_Location ()); long delimiter; int end, character = 0, type = 0; try { // Possible IOException from the String_Buffer_Reader. // Find the beginning of the datum string. if (Is_End (delimiter = Next_Location (skip_whitespace_and_comments (Next_Location ())))) { if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Value: " + delimiter + " Is_End\n<<< Get_Datum: Next_Loction = " + delimiter); return null; } if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Datum starts at location " + delimiter); // Check the beginning of the datum string. switch (Char_At (delimiter)) { // End of PVL statement cases: case STATEMENT_END_DELIMITER: // Nothing to get. break; // Syntax error cases: case PARAMETER_NAME_DELIMITER: case PARAMETER_VALUE_DELIMITER: case SET_START_DELIMITER: case SET_END_DELIMITER: case SEQUENCE_START_DELIMITER: case SEQUENCE_END_DELIMITER: case UNITS_START_DELIMITER: case UNITS_END_DELIMITER: case NUMBER_BASE_DELIMITER: /* Note: It would be possible here to be VERY tolerant and just take these illegal characters as the first character of an identifier string, but this seems unlikely in any case. */ throw new PVL_Exception ( ID, PVL_Exception.ILLEGAL_SYNTAX, "When a value datum was expected.", delimiter ); // Quoted string cases: case TEXT_DELIMITER: type = Value.TEXT; case SYMBOL_DELIMITER: if (type == 0) type = Value.SYMBOL; if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Getting a quoted string."); String // CAUTION: Get_Quoted_String will update the Next_Location. string = Get_Quoted_String (); The_Value.set_data (string); The_Value.set_type (type); return The_Value; // Numeric value or identifier: default: // Find the value string delimiter. if ((delimiter = Skip_Until (Next_Location (), PARAMETER_VALUE_DELIMITERS)) < 0) // Hit the end of input delimiter = End_Location (); if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Numeric value or symbol ends at location " + delimiter); String value = Substring (Next_Location (), delimiter); if ((end = value.indexOf (COMMENT_START_DELIMITERS)) > 0) { // Only take the part up to the comments. value = value.substring (0, end); delimiter = Next_Location () + end; } if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: value string - " + value); if (! Verbatim_Strings) // Convert escape sequences to special characters. value = translate_from_escape_sequences (new String_Buffer (value)).toString (); if (Strict || ! All_Values_Strings) { /*.................................................................. Try for a number. */ if ((end = value.indexOf (NUMBER_BASE_DELIMITER)) > 0) { /*.............................................................. Probable base notation value ([sign]Base#value#). */ if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Probable base notation value."); try { int base = Integer.parseInt (value.substring (0, end)); if ((character = ++end) >= value.length ()) throw new NumberFormatException (); if ((end = value.indexOf (NUMBER_BASE_DELIMITER, end)) < 0) end = value.length (); long number = Long.parseLong (value.substring (character, end), Math.abs (base)); if (base < 0) { base = -base; number = -number; } The_Value.set_data (new Long (number)); The_Value.Base (base); The_Value.set_type (Value.INTEGER); if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Integer = " + number + " with base " + base); break; } catch (NumberFormatException exception) {/* Must be a symbol */} } else { /*.............................................................. Try for an integer. */ try { The_Value.set_data (Long.valueOf (value)); The_Value.Base (10); The_Value.set_type (Value.INTEGER); if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Integer = " + (Long)The_Value.Data ()); break; } catch (NumberFormatException exception) { /*.......................................................... Try for a real number. */ try { The_Value.set_data (Double.valueOf (value)); The_Value.set_type (Value.REAL); if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: Real = " + (Double)The_Value.Data ()); break; } catch (NumberFormatException accept) {/* Must be a symbol */} } } } /*...................................................................... Couldn't make a number. It's a string. */ The_Value.set_data (value); if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("Get_Datum: String = " + (String)The_Value.Data ()); /* Check for possible date and time string. >>> WARNING <<< This is a cursory check, it's not determinate. */ if (new String_Buffer (value).skip_until (0, DATE_TIME_DELIMITERS) > 0) The_Value.set_type (Value.DATE_TIME); else The_Value.set_type (Value.IDENTIFIER); // Check for reserved characters. if ((character = Bad_Character (value)) >= 0) { Warning ( PVL_Exception.RESERVED_CHARACTER, "At character " + character + " of the datum \"" + new String_Buffer (value).special_to_escape () + "\"", Next_Location () + character ); if (Strict) throw Last_Warning; } } if ((DEBUG & DEBUG_DATUM) != 0) System.out.println ("<<< Get_Datum: Next_Location = " + delimiter); Next_Location (delimiter); } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Datum.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } return The_Value; } /*------------------------------------------------------------------------------ */ /** Gets a units description String from the source of PVL statements.

    After skipping over any whitespace, the next character must start a units description sequence or nothing (null) is returned. A units description sequence starts after a {@link #UNITS_START_DELIMITER UNITS_START_DELIMITER} and ends before a {@link #UNITS_END_DELIMITER UNITS_END_DELIMITER}. A units description sequence without the closing UNITS_END_DELIMITER will result in a MISSING_UNITS_END exception in Strict mode; otherwise a Warning is registered and the next {@link #PARAMETER_VALUE_DELIMITER PARAMETER_VALUE_DELIMITER}, any of the SET/SEQUENCE/UNITS START/END delimiters, {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER}, or the end of the input stream is taken as the end of the units description. Note: Though an effort is made to recover from encountering an unending units description, this will only be effective when no other normally closed units descripiton occurs in the input stream (if a normally closed units descripiton does occur after an unclosed units description, the latter will be taken as the end of the former), and in this case the input stream will have been read into memory until it is empty.

    Sequential comments, with nothing but white space intervening, are accumulated with a single new-line ('\n') chararacter separating them in the resulting String that is returned. In Strict mode comments that wrap across line breaks cause an exception. When Verbatim_Strings are not enabled whitespace is trimmed from the end of each comment (but not the beginning), and escape sequences are translated into their corresponding special characters.

    If a units descripiton is found the Next_Location of the input stream is moved to the position immediately following it. The units description is trimmed of leading and trailing whitespace. If Verbatim_Strings is not enabled, then all comment sequences are removed from the units description String, all whitespace sequences are collapsed to a single space (' ') character, and escape sequences are substituted for their corresponding special characters.

    @return A units description String, or null if no units description occurs before the next PVL item or the end of input. @throws PVL_Exception

    MISSING_UNITS_END
    A units description does not have a UNITS_END_DELIMITER.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see #Verbatim_Strings(boolean) @see #translate_from_escape_sequences(String_Buffer) */ public String Get_Units () throws PVL_Exception { if (Is_Empty ()) return null; if ((DEBUG & DEBUG_UNITS) != 0) System.out.println (">>> Get_Units: Next_Location = " + Next_Location ()); String_Buffer units = null; long delimiter, end; // Find the beginning of the units string. if (Is_End (delimiter = skip_whitespace_and_comments (Next_Location ())) || Char_At (delimiter) != UNITS_START_DELIMITER) { if ((DEBUG & DEBUG_UNITS) != 0) System.out.println ("<<< Get_Units: Next_Location = " + Next_Location ()); return null; } delimiter++; try { // Possible IOException from the String_Buffer_Reader. // Find the end of the units string. if ((end = Location_Of (delimiter, UNITS_END_DELIMITER)) < 0) { Warning ( PVL_Exception.MISSING_UNITS_END, "For value units starting with \"" + Substring (delimiter - 1, Math.min (delimiter + 19, End_Location ())) + "\" ...", delimiter - 1 ); if (Strict) throw Last_Warning; /* Lacking a formal units string end marker just find the next non-white_space parameter value delimiter. */ for (end = delimiter; (end = Skip_Until (end, PARAMETER_VALUE_DELIMITERS)) >= 0; end = Skip_Over (end, WHITESPACE)) if (end != Skip_Until (end, WHITESPACE)) break; if (end < 0) end = End_Location (); // Get the units string. units = new String_Buffer (Substring (delimiter, end)); } else units = new String_Buffer (Substring (delimiter, end++)); if ((DEBUG & DEBUG_UNITS) != 0) System.out.println ("Get_Units: " + units.toString ()); // Move the Next_Location to after the end of the units string. Next_Location (end); } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Units.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } units.trim (); // Remove leading and trailing whitespace. if (! Verbatim_Strings) { // Collapse each comments sequence to a single space. int first, last; while ((first = units.skip_until (0, COMMENT_START_DELIMITERS)) >= 0) { if ((last = units.skip_until (first, COMMENT_END_DELIMITERS)) < 0) last = units.length (); else last += COMMENT_END_DELIMITERS.length (); units.replace (first, last, " "); } /* Replace all whitespace sequences with a single space, and convert all escape sequences to their special characters. Note: The escape sequences are converted last so any resulting whitespace will not be collapsed away. */ translate_from_escape_sequences (units.replace_span (0, WHITESPACE, " ")); } if ((DEBUG & DEBUG_UNITS) != 0) System.out.println ("<<< Get_Units: Next_Location = " + Next_Location ()); return (units == null) ? null : units.toString (); } /*------------------------------------------------------------------------------ */ /** Gets a quoted String from the source of PVL statements.

    The next non-whitespace character is taken to be the "quote" character. The characters following the first quote character up to but not including the next, non-escaped (not preceeded by a backslash, '\') quote character are the quoted string. If the closing quote character can not be found, then a MISSING_QUOTE_END Warning will be registered (the exception will be thrown in Strict mode) and the quoted string will end at the end of the input stream. Note: The lack of a closing quote character will cause the entire input stream to be read into memory until it is emtpy. The Next_Location is moved to the position immediately following the last quote character.

    If Verbatim_Strings is not enabled then line break sequences (one or more sequential line breaks) and any surrounding whitespace (whitespace ending the last line and beginning the next line) are replaced with a single space (' ') character. If, however, String_Continuation is enabled and the last non-whitespace character before the line break sequence is a {@link #STRING_CONTINUATION_DELIMITER STRING_CONTINUATION_DELIMITER} then no space remains (i.e. the string ending with the last non-whitespace character on the last line is continued with the first non-whitspace character on the next line). In addition, escape sequences are translated to their corresponding special characters. Sequences of characters bracketed by {@link #VERBATIM_STRING_DELIMITERS VERBATIM_STRING_DELIMITERS} are taken verbatim; they are subject to neither end of line treatment nor escape sequence translation.

    @return A String, or null if no non-whitespace character occurs before the end of input. @throws PVL_Exception

    MISSING_QUOTE_END
    A closing quote was not found in the input stream.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    @see #Verbatim_Strings(boolean) @see #String_Continuation(boolean) @see String_Buffer#escape_to_special() */ public String Get_Quoted_String () throws PVL_Exception { if (Is_Empty ()) return null; if ((DEBUG & DEBUG_QUOTED_STRING) != 0) System.out.println (">>> Get_Quoted_String"); long location, start, end; String_Buffer quote = null; try { // Possible IOException from the String_Buffer_Reader. // The first non-whitespace character is the quotation mark. if (Is_End (start = location = Next_Location (Skip_Over (Next_Location (), WHITESPACE)))) { if ((DEBUG & DEBUG_QUOTED_STRING) != 0) System.out.println ("Get_Quoted_String: " + start + " Is_End\n<<< Get_Datum: Next_Loction = " + start); return null; } if ((DEBUG & DEBUG_QUOTED_STRING) != 0) System.out.println ("Get_Quoted_String: Quote character is >" + Char_At (start) + "< at location " + start); // Find the matching closing quote. while ((location = Location_Of (++location, Char_At (start))) >= 0) // Allow for escaped quotation mark. if (Char_At (location - 1) != '\\') break; if (location < 0) { // No end quote. Warning ( PVL_Exception.MISSING_QUOTE_END, "For the quoted string starting with " + Substring (start, Math.min (start + 20, End_Location ())) + " ...", start ); if (Strict) throw Last_Warning; location = End_Location (); } // Make a duplicate of the quoted string. quote = new String_Buffer (Substring (++start, location)); if ((DEBUG & DEBUG_QUOTED_STRING) != 0) System.out.println ("Get_Quoted_String: " + quote.toString ()); // Move the Next_Location past the quoted string. Next_Location (location + 1); } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In Get_Quoted_String.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } if (! Verbatim_Strings) { /* Compress out line wrap effects. Sections between VERBATIM_STRING_DELIMITERS are not modified. */ int first = 0, last, delimiters_length = Parser.VERBATIM_STRING_DELIMITERS.length (); String_Buffer section; for (last = quote.index_of (first, VERBATIM_STRING_DELIMITERS); first < quote.length (); last = quote.index_of (first = last, VERBATIM_STRING_DELIMITERS)) { if (last < 0) // This section extends to the end of the string. last = quote.length (); section = new String_Buffer (quote.substring (first, last)); int begin = 0, index; while ((index = section.skip_until (begin, LINE_DELIMITERS)) >= 0) { // Backup over any trailing white space. if ((begin = section.skip_back_over (index, WHITESPACE)) >= 0) { if (! String_Continuation || section.charAt (begin) != STRING_CONTINUATION_DELIMITER) { if (section.charAt (begin) != STRING_CONTINUATION_DELIMITER || begin == 0 || section.charAt (begin - 1) == ' ') // Allow one space. section.setCharAt (++begin, ' '); begin++; } } else begin = 0; /* Skip over any whitespace leading the next line. Note that skipping over white space here will also skip over any redundant line breaks. */ index = section.skip_over (index, WHITESPACE); // Delete the extra whitespace. section.delete (begin, index); } // Convert escape sequences to special characters. section.escape_to_special (); // Replace the section in the string. quote.replace (first, last, section.toString ()); last = first + section.length (); // Skip the next, verbatim, section. if ((last += delimiters_length) == quote.length () || (last = quote.index_of (last, VERBATIM_STRING_DELIMITERS)) < 0 || (last += delimiters_length) == quote.length ()) // No more sections. break; } } if ((DEBUG & DEBUG_QUOTED_STRING) != 0) System.out.println ("<<< Get_Quoted_String: Next_Location = " + Next_Location ()); return quote.toString (); } /*============================================================================== Utility functions: */ /** Gets the next location in the PVL source stream following any sequence of whitespace and/or comments. A {@link #STATEMENT_CONTINUATION_DELIMITER STATEMENT_CONTINUATION_DELIMITER} and any {@link #Crosshatch_Comments(boolean) Crosshatch_Comments} (if enabled) are included in the whitespace category. Note: As with the Get_Comments method, a comment without a closing sequence is taken to end and the next line break, {@link #STATEMENT_END_DELIMITER STATEMENT_END_DELIMITER}, or the end of the input stream; but this condition will cause the entire input stream to be read into memory.

    @param location The starting location from which to skip over whitespace and comments. @return The location of the next character after any whitespace or comments. @throws PVL_Exception

    MISSING_COMMENT_END
    A comment does not have COMMENT_END_DELIMITERS and Strict mode is enabled.
    FILE_IO
    If an IOException occurred in the String_Buffer_Reader.
    */ public long skip_whitespace_and_comments ( long location ) throws PVL_Exception { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Parser.skip_whitespace_and_comments: " + location); long comment_end; try { // Possible IOException from the String_Buffer_Reader. while (true) { // Find the beginning of the comment string. if (Is_End (location = skip_crosshatch_comments (location)) || ! Equals (location, COMMENT_START_DELIMITERS)) break; if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Comment at " + location); // Find the end of the comment string. location += COMMENT_START_DELIMITERS.length (); if ((comment_end = Location_Of (location, COMMENT_END_DELIMITERS)) < 0) { // No comment end. Warning ( PVL_Exception.MISSING_COMMENT_END, "For comment starting with \"" + Substring (location, Math.min (location + 20, End_Location ())) + "\" ...", location ); if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("Parser.skip_whitespace_and_comments:\n" + Last_Warning.getMessage ()); if (Strict) throw Last_Warning; // Assume it ends at the end of the line. if ((comment_end = Skip_Until (location, LINE_DELIMITERS + STATEMENT_END_DELIMITER)) < 0) location = End_Location (); location = Skip_Over (comment_end, LINE_DELIMITERS + STATEMENT_END_DELIMITER); } else location = comment_end + COMMENT_END_DELIMITERS.length (); if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" ends at " + location); } } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In skip_whitespace_and_comments.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("<<< Parser.skip_whitespace_and_comments: " + location); return location; } private long skip_crosshatch_comments ( long location ) throws PVL_Exception { try { // Possible IOException from the String_Buffer_Reader. if (Strict || ! Crosshatch_Comments) // Just skip whitespace. location = Skip_Over (location, WHITESPACE + STATEMENT_CONTINUATION_DELIMITER); else { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Parser.skip_crosshatch_comments: " + location); while (true) { // Find the beginning of the comment string. if (Is_End (location = Skip_Over (location, WHITESPACE + STATEMENT_CONTINUATION_DELIMITER)) || Char_At (location) != CROSSHATCH) break; // A crosshatch comment extends to the end of the line. if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Crosshatch comment at " + location); location = Skip_Until (location, LINE_BREAK); if (location < 0) location = End_Location (); if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" ends at " + location); } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("<<< Parser.skip_crosshatch_comments: " + location); } } catch (IOException exception) { // From the String_Buffer_Reader. throw new PVL_Exception ( ID, PVL_Exception.FILE_IO, "In skip_whitespace_and_comments.\n" + ((exception.getMessage () == null) ? "" : "\n" + exception.getMessage ()) ); } return location; } /** Translates escape sequences in a String_Buffer to their corresponding special characters. The {@link String_Buffer#escape_to_special() escape_to_special} method is used to translate escape sequences to special characters. However the occurance of {@link Parser#VERBATIM_STRING_DELIMITERS VERBATIM_STRING_DELIMITERS} starts a sequence of characters that are taken verbatim (they are not translated) up to the next VERBATIM_STRING_DELIMITERS or the end of the string (the VERBATIM_STRING_DELIMITERS are dropped).

    @param string The String_Buffer to be translated. @return The translated String_Buffer. @see String_Buffer#escape_to_special() */ public static String_Buffer translate_from_escape_sequences ( String_Buffer string ) { if (string == null) return string; int first = 0, last, length, delimiters_length = VERBATIM_STRING_DELIMITERS.length (); String_Buffer section; // Find sections delimited by VERBATIM_STRING_DELIMITERS. for (last = string.index_of (first, VERBATIM_STRING_DELIMITERS); first < string.length (); last = string.index_of (first = last, VERBATIM_STRING_DELIMITERS)) { if (last < 0) // This section extends to the end of the string. last = string.length (); section = new String_Buffer (string.substring (first, last)); length = section.length (); // Convert escape sequences to special characters. section.escape_to_special (); if (section.length () != length) { // The section changed; replace it in the string. string.replace (first, last, section.toString ()); last = first + section.length (); } // Skip the next, verbatim, section. if ((last += delimiters_length) >= string.length () || (last = string.index_of (last, VERBATIM_STRING_DELIMITERS)) < 0 || (last += delimiters_length) == string.length ()) // No more sections. break; } string.replace (0, VERBATIM_STRING_DELIMITERS, ""); return string; } /** Checks a String for any bad character. A bad character is one of the {@link #RESERVED_CHARACTERS RESERVED_CHARACTERS} or a non-printable character.

    @param string The String to check. @return The index of the first bad character found in the string, or -1 if no bad characters were found. */ public static int Bad_Character ( String string ) { if (string != null) { for (int index = 0; index < string.length (); index++) { char character = string.charAt (index); if (! isprint (character) || RESERVED_CHARACTERS.indexOf (character) >= 0) return index; } } return -1; } /** Tests if a character is printable: in the ASCII range from the space character (' ') to the tilde character ('~') inclusive.

    @param character The char to test. @return true if the character is printable; false otherwise. */ public static boolean isprint ( char character ) { return character >= ' ' && character < 0x7F; } /*------------------------------------------------------------------------------ */ /** Gets the Parameter classification code corresponding to the specified special Parameter name String. The special Parameter names and their classification codes are:

    Begin_Object or BeginObject - BEGIN_OBJECT
    Object - OBJECT
    End_Object or EndObject - END_OBJECT
    Begin_Group or BeginGroup - BEGIN_GROUP
    Group - GROUP
    End_Group or EndGroup - END_GROUP
    End - END_PVL

    The names are not case sensitive.

    @param name A String that may be a special Parameter name. @return the Parameter classification code associated with the special Parameter name; or -1 if the name is not a special Parameter name. @see Parameter */ public static int Special_Classification ( String name ) { if (name != null) { for (int element = 0; element < SPECIAL_NAMES.length; element++) if (name.equalsIgnoreCase (SPECIAL_NAMES[element])) return SPECIAL_CLASSIFICATIONS[element]; } return -1; } /** Gets the special Parameter name String for the specified Parameter classification code.

    @param classification A Parameter classification code int. @return The special Parameter name String associated with the classification code; or null if the classification code is not associated with a special Parameter name. @see #Special_Classification(String) */ public static String Special_Name ( int classification ) { for (int element = 0; element < SPECIAL_CLASSIFICATIONS.length; element++) if (classification == SPECIAL_CLASSIFICATIONS[element]) return SPECIAL_NAMES[element]; return null; } /*------------------------------------------------------------------------------ */ private void Warning ( String description, String explanation, long location ) { Last_Warning = new PVL_Exception ( ID, description, explanation, location ); if ((DEBUG & DEBUG_WARNINGS) != 0) System.out.println ("Parser.Warning:\n" + Last_Warning); if (First_Warning == null) First_Warning = Last_Warning; } } // class Parser pirl-2.3.8/PIRL/PVL/Invalid_Map_Syntax.java0000644000175000017500000001035311742734277020167 0ustar mathieumathieu/* Invalid_Map_Syntax PIRL CVS ID: Invalid_Map_Syntax.java,v 1.5 2012/04/16 06:14:23 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; /** The Exeception that is thrown if a PVL_to_DB map parameter contains invalid syntax.

    @author Bradford Castalia - UA/PIRL @version 1.5 @see PVL_to_DB */ public class Invalid_Map_Syntax extends Exception { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Invalid_Map_Syntax (1.5 2012/04/16 06:14:23)"; private String Reference = null, Pathname = null, Value = null; /** Constructs an Invalid_Map_Syntax exception with the specified detail message.

    The exception's Reference, Pathname and Value will be null.

    @param message The detail message; retrievable by the {@link Throwable#getMessage()} method. */ public Invalid_Map_Syntax ( String message ) {super (message);} /** Constructs an Invalid_Map_Syntax exception with the specified detail message and reference.

    The exception's Pathname and Value will be null.

    @param message The detail message; retrievable by the {@link Throwable#getMessage()} method. @param reference The reference String that contains the invalid syntax; retrievable by the {@link #Reference()} method. */ public Invalid_Map_Syntax ( String message, String reference ) { super (message); Reference = reference; } /** Constructs an Invalid_Map_Syntax exception with the specified detail message, reference, and map parameter.

    The {@link #Pathname() Pathname} and {@link #Value() Value} Strings of the parameter are saved.

    @param message The detail message; retrievable by the {@link Throwable#getMessage()} method. @param reference The reference String that contains the invalid syntax; retrievable by the {@link #Reference()} method. @param parameter The map parameter that contains the invalid syntax. */ public Invalid_Map_Syntax ( String message, String reference, Parameter parameter ) { super (message); Reference = reference; Parameter (parameter); } /** Gets the reference with the invalid syntax.

    @return The reference String with the invalid syntax that caused the exception. */ public String Reference () {return Reference;} /** Gets the pathname of the map parameter with the invalid syntax.

    @return The pathname of the map parameter that contained the reference with invalid syntax. */ public String Pathname () {return Pathname;} /** Gets the value of the map parameter with the invalid syntax.

    @return The value String of the map parameter that contained the reference with invalid syntax. */ public String Value () {return Value;} /** Saves the pathname and value of the parameter.

    If the parameter is an Aggregate, the value will be null. If the parameter Value is an Array, the full reprepresentation of the Array will be saved.

    @param parameter The map parameter that contains the invalid syntax. */ public void Parameter ( Parameter parameter ) { Pathname = parameter.Path_Name (); if (parameter.Is_Aggregate ()) Value = null; else { Value value = null; try {value = parameter.Value ();} catch (PVL_Exception exception) {/* Checked for Aggregate above. */} if (value.Is_Array ()) Value = value.Description (); else { try {Value = value.String_Data ();} catch (PVL_Exception exception) {/* Checked for Array above. */} } } } } pirl-2.3.8/PIRL/PVL/package.html0000644000175000017500000001076011742734277016056 0ustar mathieumathieu The PIRL Parameter Value Language package provides for the input, output and manipulation of parameters and their data values. Perhaps one of the most common concepts in scientific research is the named parameter that refers to a value of some type:

    Name = Value

    The Name is a single textual token that provides the referential handle to the value. The choice of parameter names for values is completely arbitrary, though it may be guided by various usage conventions. Thus there is no guarantee that a parameter of any given name will refer to a value of any particular type. While this provides for complete freedom in the use of parameter names, it also allows for considerable confusion.

    The Value may be quite simple - for example, a single decimal integer - or very complex - for example the complete characterization of an instrument - yet the type of value must be specifically defined to avoid any ambiguity in its use. Thus the value definition mechanism must be broadly encompassing to allow for the needed freedom in the use of values, while providing an interpretation that allows for no misinterpretation.

    A Parameter Value Language (PVL) provides a text syntax for associating a named parameter with a specifically defined type of value. While there are various forms of parameter value language to choose from, the needs of the Planetary Image Research Lab (PIRL) to handle files distributed by the Planetary Data System (PDS) led directly to the PVL specified for use in PDS products.

    The image files (and others) distributed on PDS CDs contain an initial label section that provides descriptive information about the image, including parameters necessary for the correct processing of the binary data in the file. The syntax of the PVL used by PDS products has been specified by the Consultative Committee for Space Data Systems in the Blue Book "Parameter Value Language Specification (CCSDS0006,8)", June 2000 [CCSDS 641.0-B-2] and Green Book "Parameter Value Language - A Tutorial", May 1992 [CCSDS 641.0-G-1] documents. PVL has been accepted by the International Standards Organization (ISO), as a Draft Standard (ISO/CD 14961:1997). The Object Definition Language (ODL) used to describe the metadata in Earth Observing System (EOS) image files is also an implementation of PDS PVL, though it is embedded in the HDF-EOS extension to the NCSA HDF format.

    The actual form of the PDS PVL, however, has evolved somewhat since it was first used in early products, such as imagery from the Voyager and Viking missions. This resulted in inconsistencies that made it difficult to write software which could handle all PDS products. The PIRL PVL package uses controlled tolerance during PVL statement parsing that is able to successfully read any PVL, including all PDS products. It also provides controlled strictness when generating PVL statements, making it suitable for creating new products that employ PVL. A full complement of Parameter and Value object creation, modification, and searching methods are provided. pirl-2.3.8/PIRL/PVL/tests/0000755000175000017500000000000012052546517014724 5ustar mathieumathieupirl-2.3.8/PIRL/PVL/tests/Makefile0000644000175000017500000000116710122112102016340 0ustar mathieumathieu# Makefile for Java classes # # CVS ID: Makefile,v 1.3 2004/09/15 19:13:06 castalia Exp # # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm JPATH ?= .:../../..:$(JCM) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = ../Parameter.class \ ../Selector.class \ ../Selection.class \ ../Value.class \ ../Parser.class \ ../Lister.class \ ../PVL_Exception.class TESTS = PVL.class \ Parameter_test.class \ Value_test.class all: ${TESTS} ${TESTS}: ${CLASSES} classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/PVL/tests/PVL.java0000644000175000017500000001414111742734277016240 0ustar mathieumathieu/* PVL CVS ID: PVL.java,v 1.8 2012/04/16 06:14:23 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.PVL.*; import PIRL.Utilities.Streams; import java.io.File; public class PVL { public static void main (String[] arguments) { if (arguments.length == 0) Usage (); try { String source = null; Parameter selection = new Parameter (); Selector criteria = new Selection (); boolean indent = true, strict = false, select = false, quiet = false; for (int argument = 0; argument < arguments.length; argument++) { if (arguments[argument].startsWith ("-") && arguments[argument].length () > 1) { switch (arguments[argument].charAt (1)) { case 'n': case 'f': criteria.Specific (false); case 'N': case 'F': select = true; criteria.Name (true); if (++argument == arguments.length) Usage (); selection.Name (arguments[argument]); break; case 'C': case 'c': select = true; criteria.Classification (true); if (++argument == arguments.length) Usage (); System.out.println ("Classification = " + arguments[argument]); switch (arguments[argument].charAt (0)) { case 'A': case 'a': criteria.Specific (false); if (arguments[argument].charAt (1) == 'G' || arguments[argument].charAt (1) == 'g') selection.Classification (Parameter.GROUP); else selection.Classification (Parameter.ASSIGNMENT); break; case 'G': case 'g': criteria.Specific (true); selection.Classification (Parameter.GROUP); break; case 'O': case 'o': criteria.Specific (true); selection.Classification (Parameter.OBJECT); break; case 'T': case 't': selection.Classification (Parameter.TOKEN); break; case 'U': case 'u': selection.Classification (Parameter.UNKNOWN); break; default: Usage (); } break; case 'v': criteria.Specific (false); case 'V': select = true; criteria.Value (true).Data (true); if (++argument == arguments.length) Usage (); Parser value_string = new Parser (arguments[argument]); selection.Value (new Parser (arguments[argument]).Get_Value ()); break; case 'A': case 'a': if (! arguments[argument].equalsIgnoreCase ("-AND")) Usage (); criteria.And (true); break; case 'O': case 'o': if (! arguments[argument].equalsIgnoreCase ("-OR")) Usage (); criteria.And (false); break; case 'I': case 'i': indent = false; if ((argument + 1) < arguments.length && arguments[argument + 1].charAt (0) != '-') { argument++; if (arguments[argument].indexOf ('N') >= 0 || arguments[argument].indexOf ('n') >= 0) indent = true; } break; case 'S': case 's': strict = true; if ((argument + 1) < arguments.length && arguments[argument + 1].charAt (0) != '-') { argument++; if (arguments[argument].indexOf ('F') >= 0 || arguments[argument].indexOf ('f') >= 0) strict = false; } break; case 'P': case 'p': criteria.Pattern_Match (true); break; case 'Q': case 'q': quiet = true; break; default: System.err.println ("Unknown option: " + arguments[argument]); case 'H': case 'h': Usage (); } } else if (source == null) source = arguments[argument]; else { System.err.println ("Multiple PVL sources -\n" + source + '\n' +"and\n" + arguments[argument]); Usage (); } } if (source == null) { System.err.println ("A PVL source must be specified."); Usage (); } // Ingest: Parameter parameter = new Parameter ( source, new Parser (Streams.Get_Stream (source)) ); if (parameter.Warning () != null) { if (parameter.Warning ().Message ().equals (PVL_Exception.SIZED_RECORDS)) System.out.println (parameter.Warning ()); else System.out.println ( ">>> WARNING <<<\n" + parameter.Warning ().getMessage () + "\n" ); } if (select) { String name; Parameter param = null; while ((param = parameter.Find (selection, criteria, param)) != null) { name = param.Name (); param.Name (param.Path_to_Name ().substring (source.length () + 1) + name); if (quiet) param.Comments (null); param.Write (indent, strict); param.Name (name); } } else { parameter.Write (indent, strict); new Parameter ().Classification (Parameter.END_PVL).Write (); } } // try catch (Exception exception) { System.out.println ("PVL error -\n" + exception.getMessage () + "\n"); System.exit (-1); } System.exit (0); } private static void Usage () { System.err.println ( "Usage: java PVL [options] \n" + " Options -\n" + " -Name | -Find \n" + " -Name or -Find is case sensitive\n" + " -name or -find is case insensitive\n" + " -Classification \n" + " The is AGgregate, Group, Object, Assignment, Token or Unknown\n" + " -Value \n" + " -AND|OR [or]\n" + " -Pattern\n" + " Comparison or strings are regular expressions\n" + " -Indent [oFf | oN] (on)\n" + " -Strict [oN | oFf] (off)\n" + " -Quiet\n" + " Suppress comments of selected parameters\n" + " -Help\n" ); System.exit (1); } } pirl-2.3.8/PIRL/PVL/tests/Value_test.java0000644000175000017500000003054311742734300017701 0ustar mathieumathieu/* Value_test CVS ID: Value_test.java,v 1.5 2012/04/16 06:14:24 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.PVL.*; import java.io.File; import java.util.Iterator; import java.util.ListIterator; import java.util.Vector; import java.io.ByteArrayOutputStream; public class Value_test { public static void main (String[] arguments) { System.out.println ("*** Value_test ***"); try { String parameter_string = "The_Value = \n" + " (101.1 , 22 , \"three\" , 'four' , five ,\n" + " {1, 2, 3, 4,\n" + " { 11, 22, 33} ,\n" + " 5} }/* should be ')' */ \n"; Parameter parameter; Value values, value, new_value, val; int value_type, count; boolean test; System.out.println ("--- parameter_string:"); System.out.println (parameter_string); System.out.println ("--- Constructing a parameter from the String:\n" + "\tparameters = new Parameter (new Parser (parameter_string));"); // Constructor: String Parser parameter = new Parameter (new Parser (parameter_string)); if (parameter.Warning () != null) { System.out.println (parameter.Warning ().getMessage ()); System.out.println ("*** Array enclosure mismatch expected."); } else System.out.println ("!!! Array enclosure mismatch expected."); System.out.println ("--- Writing the parameter:\n" + "\tparameter.Write ();"); // Write: Parameter count = parameter.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Get the Values Array from the Parameter:\n" + "\tvalues = parameter.Value ();"); values = parameter.Value (); System.out.println ("--- Writing the Values Array:\n" + "\tvalues.Write ();"); // Write: Value count = values.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Iterating"); print_values (values); System.out.println ("--- Finding 3:\n" + "\tvalue = values.Find (3);"); // Find: int value = values.Find (3); if (value == null) { System.out.println ("!!! Not found"); System.exit (1); } System.out.println ("*** Found: " + value.Type_Name () + " " + value); System.out.println ("--- Writing Parent:\n" + "\tvalue.Parent ().Write ();"); // Parent: value.Parent ().Write (); System.out.println ("--- Changing its type to TEXT:\n" + "\tvalue.Type (Value.TEXT);"); // Type: INTEGER to TEXT value.Type (Value.TEXT); System.out.println (value.Type_Name () + " " + value); if (value.Is_String ()) System.out.println ("*** Correct"); else { System.out.println ("!!! Failed"); System.exit (1); } System.out.println ("--- Writing to a ByteArrayOutputStream (sans formatting):\n" + "\ttext = new ByteArrayOutputStream ();\n" + "\tvalues.Write (text, false);"); ByteArrayOutputStream text = new ByteArrayOutputStream (); // Write: to ByteArrayOutputStream without indenting value.Write (text, false); System.out.print (text); if (text.toString ().startsWith ("\"3\"")) System.out.println ("*** Correct"); else System.out.println ("!!! Wrong. Should be quoted"); System.out.println ("--- Changing the text:\n" + "\tvalue.Data (\"'7.8'\");"); // Data: String SYMBOL value.Data ("'7.8'"); System.out.println (value.Type_Name () + " " + value); // String_Data: if (value.String_Data ().equals ("7.8") && value.Is_Symbol ()) System.out.println ("*** Correct"); else { System.out.println ("!!! Failed"); System.exit (1); } System.out.println ("--- Changing the type to REAL:\n" + "\tvalue.Type (Value.REAL);"); // Type: STRING to REAL value.Type (Value.REAL); System.out.println (value.Type_Name () + " " + value); // double_Data: if (value.Is_Real () && value.double_Data () == 7.8) System.out.println ("*** Correct"); else { System.out.println ("!!! Failed"); System.exit (1); } System.out.println ("--- Finding this value in the values array:\n" + "\tvalue = values.Find (value);"); // Find: Value value = values.Find (value); if (value == null) { System.out.println ("!!! Not found"); System.exit (1); } System.out.println ("*** Found: " + value.Type_Name () + " " + value); System.out.println ("--- Finding no-such-value:\n" + "\tvalues.Find (\"no-such-value\")"); if (values.Find ("no-such-value") == null) System.out.println ("*** Not found (correct)"); else System.out.println ("!!! Found (shouldn't have)"); System.out.println ("--- Make a new value:\n" + "\tnew_value = new Value (\"\\\"This is a new value\\\"\");"); // Constructor: String new_value = new Value ("\"This is a new value\""); System.out.println (new_value.Type_Name () + " " + new_value); System.out.println ("--- Add the test values to it:\n" + "\tvalue_type = new_value.Type ();\n" + "\tnew_value.Add (values);"); value_type = new_value.Type (); // Add: ARRAY to STRING try {new_value.Add (values);} catch (PVL_Exception exception) { System.out.println (exception.getMessage () + "\n"); System.out.println ("!!! Failed"); System.exit (1); } print_values (new_value); if (new_value.Is_Array ()) System.out.println ("*** Correct"); else { System.out.println ("!!! Wrong. Should now be an Array"); System.exit (1); } System.out.println ("--- Remove the test values:\n" + "\tnew_value.Remove (values);"); // Remove: Value new_value.Remove (values); System.out.print (new_value.Type_Name () + " "); new_value.Write (); // getChildCount: if (new_value.getChildCount () == 1) System.out.println ("*** Correct"); else { System.out.println ("!!! Wrong. Should only have one entry instead of " + new_value.getChildCount ()); System.exit (1); } System.out.println ("--- Try to Add a Vector containing a Parameter:\n" + "\tVector vector = new Vector ();\n" + "\tvector.add (parameter);\n" + "\tvalues.Add (vector);"); Vector vector = new Vector (); vector.add (parameter); // Add: Vector with invalid object test = true; try {new_value.Add (vector);} catch (PVL_Exception exception) { System.out.println (exception.getMessage () + "\n"); System.out.println ("*** Correct"); test = false; } if (test) { System.out.println ("!!! Wrong: Should have failed"); System.exit (1); } System.out.println ("--- Restore it to its former type:\n" + "\tnew_value.Type (value_type);"); // Type: ARRAY to TEXT new_value.Type (value_type); System.out.print (new_value.Type_Name () + " "); new_value.Write (); if (new_value.Type () == value_type) System.out.println ("*** Correct"); else { System.out.println ("!!! Wrong. Should be type " + Value.Type_Name (value_type)); System.exit (1); } System.out.println ("Current values:"); print_values (values); System.out.println ("--- Add the ARRAY (Vector) from the values:\n" + "\tnew_value.Add (values.Vector_Data ());"); test = true; // Vector_Data: // Add: Vector to TEXT try {new_value.Add (values.Vector_Data ());} catch (PVL_Exception exception) { System.out.println (exception.getMessage () + "\n"); System.out.println ("!!! Failed"); test = false; } if (test) { new_value.Write (); if (new_value.getChildCount () == (values.getChildCount () + 1)) System.out.println ("*** Correct"); else { System.out.println ("!!! Wrong: Should have " + (values.getChildCount () + 1) + " array values"); System.exit (1); } } System.out.println ("--- Remove the first entry :\n" + "\tnew_value.Remove (0);"); // Remove: index // Constructor: Value clone if (new_value.Remove (0) == null) { System.out.println ("!!! Failed"); System.exit (1); } else { new_value.Write (); System.out.println ("*** Correct"); } System.out.println ("--- Copy the new_value to values:\n" + "\tvalues = new Value (new_value);"); values = new Value (new_value); values.Write (); System.out.println ("--- Testing equality of copy:\n" + "\tvalues.equals (new_value)"); // equals: if (values.equals (new_value)) System.out.println ("*** true"); else System.out.println ("!!! false"); System.out.println ("--- Change the new_value array element 11 INTEGER to REAL:\n" + "\tnew_value.Find (11).Type (Value.REAL);"); new_value.Find (11).Type (Value.REAL); System.out.println ("--- Testing equality again:\n" + "\tvalues.equals (new_value)"); if (values.equals (new_value)) System.out.println ("!!! true (should be false)"); else System.out.println ("*** false"); System.out.println ("--- Testing equivalence of copy:\n" + "\tvalues.equalsIgnoreCase (new_value)"); // equalsIgnoreCase: if (values.equalsIgnoreCase (new_value)) System.out.println ("*** true"); else System.out.println ("!!! false"); System.out.println ("--- Empty new_value array and reset to TEXT:\n" + "\tnew_value.Remove_All ().Data (\"\\\"This is a new value\\\"\");"); // Remove_All: // Data: empty ARRAY to TEXT new_value.Remove_All ().Data ("\"This is a new value\""); System.out.println (new_value.Type_Name () + " " + new_value); if (new_value.Is_Text ()) System.out.println ("*** Correct"); else { System.out.println ("!!! Failed"); System.exit (1); } System.out.println ("--- ListIterator starting at second entry of values:\n" + "\tlist = values.listIterator (1);"); // ListIterator: ListIterator list = values.listIterator (1); System.out.println ("--- Iterating forward:\n" + "\twhile (list.hasNext ()) {\n" + "\t list.next ();"); // ListIterator: hasNext while (list.hasNext ()) { // ListIterator: next value = (Value)list.next (); System.out.println (value.Type_Name () + ": " + value); if (value.Is_Identifier ()) { System.out.println ("--- add new_value after first IDENTIFIER:\n" + "\tlist.add (new_value);"); // ListIterator: add list.add (new_value); } } System.out.println ("--- Iterating back:\n" + "\twhile (list.hasPrevious ()) {\n" + "\t list.previous ();"); // ListIterator: hasNext while (list.hasPrevious ()) { // ListIterator: next value = (Value)list.previous (); System.out.println (value.Type_Name () + ": " + value); } System.out.println ("--- Iterating foward again"); while (list.hasNext ()) { value = (Value)list.next (); System.out.println (value.Type_Name () + ": " + value); if (value.equals (new_value)) { System.out.println ("--- remove new_value:\n" + "\tlist.remove ();"); // ListIterator: remove list.remove (); } } System.out.println ("--- Writing everything"); count = values.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Iterating back again"); while (list.hasPrevious ()) { value = (Value)list.previous (); System.out.println (value.Type_Name () + ": " + value); if (value.Is_Array ()) { System.out.println ("--- Replace last ARRAY with new_value:\n" + "\tlist.set (new_value);"); list.set (new_value); break; } } System.out.println ("--- Writing everything"); count = values.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Insert the replaced value at the third entry:\n" + "\tvalues.Insert (value, 2);"); values.Insert (value, 2); count = values.Write (); System.out.println (count + " characters written.\n"); } catch (Exception exception) { System.err.println (exception.getMessage () + "\n\n"); System.exit (-1); } System.exit (0); } public static void print_values (Value value) { // Iterator: Iterator list = value.iterator (); while (list.hasNext ()) { value = (Value)list.next (); System.out.println (value.Type_Name () + ": " + value); } } } pirl-2.3.8/PIRL/PVL/tests/PVL_to_DB_test0000755000175000017500000003737211742734277017444 0ustar mathieumathieu#!/bin/perl # PVL_to_DB_test # # This procedure performs unit tests for the PVL_to_DB Java application. # # The test is performed using a local MySQL database. Access to the # database must be configured in the Database.conf file as well as for # the mysql client application used to create the test table and # confirm the database operations. A Database.conf Server may be # selected using the -Server option, otherwise the first Server listed # will be used. The mysql server host may be specified using the -Host # option, otherwise the default host, typically set in the user's # ~/.my.cnf file, will be used. The configured Server and mysql host # must, of course, be the same. # # The test.PVL_to_DB table is intially created anew. The user must have # privileges to create and drop this table, including the creation of the # containing catalog if it doesn't exist. # # A sequence of recrod insert tests is followed by a sequence of record # update tests. At the end of the tests the test table is dropped, but # not the containing catalog. # # PIRL CVS ID: PVL_to_DB_test,v 1.9 2012/04/16 06:14:23 castalia Exp # # Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the # Planetary Image Research Laboratory, Lunar and Planetary Laboratory at # the University of Arizona. # # This file is part of the PIRL Java Packages. # # The PIRL Java Packages are free software; you can redistribute them # and/or modify them under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # The PIRL Java Packages are distributed in the hope that they will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # ################################################################################ use English; print "--- PVL_to_DB test ----\n"; $Verbose = 0; $Cleanup = 1; while ($option = shift @ARGV) { if ($option =~ /^-[Vv]/) { $Verbose = 1; next; } if ($option =~ /^-[Kk]/) { $Cleanup = 0; next ; } if ($option =~ /^-[Ss]/) { if (! @ARGV || $ARGV[0] =~ /^-/) { print STDERR "Missing database server name for $option option.\n"; exit (1); } $Database_Server = shift @ARGV; next ; } if ($option =~ /^-[Hh]/) { if (! @ARGV || $ARGV[0] =~ /^-/) { print STDERR "Missing hostname for $option option.\n"; exit (1); } $Database_Host = shift @ARGV; next ; } print "Unknown argument - \"$option\"\n"; exit (1); } # PVL_to_DB configuration file. $Config_Filename = "./Database.conf"; if (! open (CONFIG_FILE, "< $Config_Filename")) { print "Unable to access the configuration file - $Config_Filename\n"; exit (1); } # Read the user's configuration file. @User_Config = ; close CONFIG_FILE; # Database client for submitting SQL statements. $SQL_Tool = "mysql"; # Add the database host specification. $SQL_Tool .= " -h $Database_Host" if $Database_Host; # Database catalog where the test table will be located. $Catalog = "test"; # Database table on which the test will be performed. $Table = "$Catalog.pvl_to_db"; # Java runtime environment. $Java_Tool = "java"; $classpath; # Provide the classpath for the database driver. $classpath = "/opt/java/mysql_connector" unless ($classpath = $ENV{"CLASSPATH"}); # Add the classpath for the PVL_to_DB class. $Java_Tool .= " -classpath ../../..:$classpath"; # Add the PVL_to_DB class. $Java_Tool .= " PIRL.PVL.PVL_to_DB"; # Add the PVL_to_DB verbose option. $Java_Tool .= " -verbose"; # Add the PVL_to_DB database server option. $Java_Tool .= " -server $Database_Server" if $Database_Server; #------------------------------------------------------------------------------- # Create the test table print "--- Create the $Table table.\n" if $Verbose; Database ("CREATE DATABASE IF NOT EXISTS $Catalog"); Database ("DROP TABLE IF EXISTS $Table"); Database ("CREATE TABLE $Table (". "ID INT AUTO_INCREMENT PRIMARY KEY,". "TEXT_1 TEXT,". "TEXT_2 TEXT,". "INT_1 INT,". "INT_2 INT)" ); $Source_Filename = "PVL_to_DB_test.source"; $Map_Filename = "PVL_to_DB_test.map"; $Config_Filename = "PVL_to_DB_test.conf"; $PVL_to_DB = $Java_Tool. " -Configure $Config_Filename". " -Map $Map_Filename ". $Source_Filename; $Total_Tests = 0; $Passed_Tests = 0; #------------------------------------------------------------------------------- # Insert print "\n>>> Insert\n"; $Description = "Insert one record, all fields (except auto-increment id)."; # Make a copy of the user's config file. Config (''); Source ( 'Text_1 = First'."\n". 'Text_2 = Second'."\n". 'Number_1 = 1000'."\n". 'Number_2 = 2000'."\n" ); Map ( "Group = $Table"."\n". ' TEXT_1 = Text_1'."\n". ' TEXT_2 = Text_2'."\n". ' INT_1 = Number_1'."\n". ' INT_2 = Number_2'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n" ); $Description = "Insert another record, using config parameter Test_Parameter."; Config ( 'Group = PVL_to_DB'."\n". ' Test_Parameter = Text'."\n". 'End_Group'."\n" ); Source ( 'Text_1 = First'."\n". 'Text_2 = Second'."\n". 'Number_1 = 1000'."\n". 'Number_2 = 2000'."\n" ); Map ( "Group = $Table"."\n". ' TEXT_1 = "${Test_Parameter}_1"'."\n". ' TEXT_2 = "${Test_Parameter}_2"'."\n". ' INT_1 = Number_1'."\n". ' INT_2 = Number_2'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 First Second 1000 2000'."\n" ); $Description = "Insert record field from Array values."; Config ( 'Group = PVL_to_DB'."\n". ' Test_Parameter = Record'."\n". 'End_Group'."\n" ); Source ( 'Record = (a, b, 1, 2)'."\n". 'Record = (AA, BB, 10, 20)'."\n" ); Map ( "Group = $Table"."\n". ' TEXT_1 = "Record"'."\n". ' TEXT_2 = "${Test_Parameter}@1[1]"'."\n". ' INT_1 = "${Test_Parameter}[2]"'."\n". ' INT_2 = "${Test_Parameter}[3]"'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 First Second 1000 2000'."\n". '3 a BB 1 2'."\n" ); #------------------------------------------------------------------------------- # Update print "\n>>> Update\n"; $Description = "Update one record."; Config ( 'Group = PVL_to_DB'."\n". ' Test_Parameter = Text'."\n". 'End_Group'."\n" ); Source ( 'Text_1 = One'."\n". 'Text_2 = Second'."\n". 'Number_1 = 1000'."\n". 'Number_2 = 2000'."\n" ); Map ( "'$Table.TEXT_1:ID=2' = ".'"${Test_Parameter}_1"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 One Second 1000 2000'."\n". '3 a BB 1 2'."\n" ); $Description = "Update with reference to a command line parameter."; $pvl_to_db = $PVL_to_DB; $PVL_to_DB .= " -Set Ident=3"; Source ( 'Number = 3'."\n" ); Map ( "Group = '$Table:ID=".'${Ident}'."'"."\n". ' INT_2 = "Number"'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 One Second 1000 2000'."\n". '3 a BB 1 3'."\n" ); $Description = "Command line parameter overriding config."; $PVL_to_DB .= " -Set INT=2"; Config ( 'INT = -1'."\n" ); Source ( 'Number = 3'."\n" ); Map ( "Group = '$Table:ID=\${Ident}'"."\n". ' INT_2 = "${INT}"'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 One Second 1000 2000'."\n". '3 a BB 1 2'."\n" ); $PVL_to_DB =$pvl_to_db; $Description = "Math expression."; Map ( "'$Table.INT_1:ID=2' = ".'"2 * 3 + 1"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 One Second 7 2000'."\n". '3 a BB 1 2'."\n" ); $Description = "Quoted string."; Source ( 'Record = (a, b, 1, 2)'."\n". 'Record = (AA, BB, 10, 20)'."\n" ); Map ( "'$Table.TEXT_1:ID=2' = ".'"\'Quoted\'"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 a BB 1 2'."\n" ); $Description = "Multi-reference with quoted strings combination."; Source ( 'Record = (R0, R1, 22, 33)'."\n" ); Map ( "'$Table.TEXT_1:ID=3' = ".'"Record[0]\' and \'Record[1]\'_\'Record[3]"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 R0 and R1_33 BB 1 2'."\n" ); $Description = "Alternate source reference."; Map ( "'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | Record"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 R0 BB 1 2'."\n" ); $Description = "Alternate source reference with quoted string."; Map ( "'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | \'alternate\'"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 alternate BB 1 2'."\n" ); $Description = "Multiple alternate source reference."; Map ( "'$Table.TEXT_1:ID=3' = ".'"NONE_SUCH | Record | \'wrong\'"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 R0 BB 1 2'."\n" ); $Description = "Multiple references with alternate sources."; Map ( "'$Table.TEXT_1:ID=3' = ".'"Record[4] | Record\',\'Record[1] | "'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 R0,R1 BB 1 2'."\n" ); $Description = "Parenthesized references."; Source ( 'Record = (A, B, C)'."\n" ); Map ( "'$Table.TEXT_1:ID=3' = ".'"(Record[0]\',\'Record[1]\',\'Record[2]) | Record"'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 A,B,C BB 1 2'."\n" ); $Description = "Parenthesized references with alternate source."; Source ( 'Record = D'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 D BB 1 2'."\n" ); $Description = "Update or Insert fallback."; Source ( 'Record = (AA, BB, 11, 22)'."\n" ); Map ( "Group = '*$Table:ID=5'"."\n". ' TEXT_1 = "Record[0]"'."\n". ' TEXT_2 = "Record[1]"'."\n". ' INT_1 = "Record[2]"'."\n". ' INT_2 = "Record[3]"'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 2000'."\n". '3 D BB 1 2'."\n". '4 AA BB 11 22'."\n" ); $Description = "Database reference in Group name."; Source ( 'Number = 3'."\n" ); Map ( "Group = '$Table:ID={$Table.INT_2:ID=3}'"."\n". ' INT_2 = Number'."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 3'."\n". '3 D BB 1 2'."\n". '4 AA BB 11 22'."\n" ); $Description = "Database reference in map source reference."; Source ( 'Number = 3'."\n" ); Map ( "Group = $Table\n". " TEXT_1 = '\"{$Table.TEXT_1:ID=1}\"'"."\n". " TEXT_2 = '\"{$Table.TEXT_2:ID=2}\"'"."\n". " INT_1 = '{$Table.INT_1:ID=3}'"."\n". " INT_2 = '{$Table.INT_2:ID=4}'"."\n". 'End_Group'."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 3'."\n". '3 D BB 1 2'."\n". '4 AA BB 11 22'."\n". '5 First Second 1 22'."\n" ); $Description = "No expression eval marker of mathematical expression."; Source ( 'Text_1 = "2/3"'."\n" ); Map ( "'$Table.TEXT_1:ID=5' = ':Text_1'"."\n" ); Done (1) if ! Check ($Description, 0, 'ID TEXT_1 TEXT_2 INT_1 INT_2'."\n". '1 First Second 1000 2000'."\n". '2 Quoted Second 7 3'."\n". '3 D BB 1 2'."\n". '4 AA BB 11 22'."\n". '5 2/3 Second 1 22'."\n" ); Done (0); #------------------------------------------------------------------------------- # Submit an SQL request to the database server. sub Database { my ($SQL) = @_; my $DB_op = "$SQL_Tool -e '".$SQL."'"; print "$DB_op\n" if $Verbose; if (system ("$DB_op") >> 8) { print "*** FAIL: You may not have the database privilege to do this.\n"; print "$DB_op\n" if ! $Verbose; exit (1); } } # Make a copy of the user's config parameters to the $Config_Filename, # adding any additional $Config PVL to the end of the file. sub Config { ($Config) = @_; if (! open (CONFIG_FILE, "> $Config_Filename") || ! print CONFIG_FILE @User_Config, $Config) { print "**** FAIL: Unable to create the config file - $Config_Filename\n"; Done (1); } close CONFIG_FILE; } # Write the $Source PVL statements to the $Source_Filename. sub Source { ($Source) = @_; if (! open (SOURCE_FILE, "> $Source_Filename") || ! print SOURCE_FILE $Source) { print "**** FAIL: Unable to create the source file - $Source_Filename\n"; Done (1); } close SOURCE_FILE; } # Write the $Map PVL statements to the $Map_Filename. sub Map { ($Map) = @_; if (! open (MAP_FILE, "> $Map_Filename") || ! print MAP_FILE $Map) { print "**** FAIL: Unable to create the map file - $Map_Filename\n"; Done (1); } close MAP_FILE; } # Check the results of the PVL_to_DB operation: # Run the $PVL_to_DB and capture its report. # Compare the specified exit status with what resulted. sub Check { my ($description, $expected_status, $expected_table) = @_; # Run the $PVL_to_DB command and capture the report. print "\n", "----------------------------------------------------------------------\n", "$PVL_to_DB\n" if $Verbose; $PVL_to_DB_report = `$PVL_to_DB`; print $PVL_to_DB_report if $Verbose; # Check the exit status. my $obtained_status = $? >> 8; $expected_status &= 0xFF; my $match = ($obtained_status == $expected_status); print "\n", "----------------------------------------------------------------------\n", "$PVL_to_DB\n", $PVL_to_DB_report if (! $Verbose && ! $match); print "--> Expected exit status $expected_status\n", " Obtained exit status $obtained_status\n" if ($Verbose || ! $match); if ($match) { # Check the table contents. print 'echo "select * from ', $Table, '" | ', $SQL_Tool, "\n" if $Verbose; my $obtained_table = qx/echo "select * from $Table" | $SQL_Tool/; $match = $obtained_table eq $expected_table; print "--> Expected table contents:\n$expected_table", " Obtained table contents:\n$obtained_table" if ($Verbose || ! $match); } print "=== Configuration: $Config_Filename\n", $Config, "=== Source: $Source_Filename\n", $Source, "=== Map: $Map_Filename\n", $Map if ($Verbose || ! $match); print "*** ", ($match ? "PASS" : "FAIL"), ": $description\n"; $Total_Tests++; $Passed_Tests++ if $match; return $match; } # End of test cleanup.: # Report the number tests passed and tried. # Delete the $Config_Filename, $Source_Filename and $Map_Filename. # Delete the test $Table from the database catalog. # Exit with the specified status value. sub Done { my ($status) = @_; print "\n", $Passed_Tests, " test", ($Passed_Tests == 1 ? "" : "s"), " passed.\n", $Total_Tests, " test", ($Total_Tests == 1 ? "" : "s"), " tried.\n"; if ($Cleanup) { # Remove the Config, Source and Map files. unlink ($Config_Filename); unlink ($Source_Filename); unlink ($Map_Filename); # Remove the test table Database ("DROP TABLE IF EXISTS $Table"); } exit ($status); } pirl-2.3.8/PIRL/PVL/tests/Parameter_test.java0000644000175000017500000004022411742734300020542 0ustar mathieumathieu/* Parameter_test CVS ID: Parameter_test.java,v 1.9 2012/04/16 06:14:24 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.PVL.*; import java.io.File; import java.io.FileOutputStream; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Vector; import java.io.ByteArrayOutputStream; public class Parameter_test { public static void main (String[] arguments) { System.out.println ("*** " + Parameter.ID); try { String parameters_string = "Group = TOP\n" + " TOP_1 = 1\n" + " Object = SUB_A\n" + " SUB_A1 = 1.0\n" + " SUB_A2 = 2.0\n" + " SUB_A3 = 3.0\n" + " End_Object = SUB_A\n" + " Group = SUB_B\n" + " SUB_B1 = one\n" + " SUB_B2 = (two, 2, to, too)\n" + " End_Object /* should be End_Group */ = SUB_B\n" + " Object = TOP_1\n" + " End_Object = TOP_1\n" + "End_Group = TOP\n"; Parameter parameters, new_param, param; int param_class, count; boolean test; System.out.println ("--- parameters_string:"); System.out.println (parameters_string); System.out.println ("--- Constructing parameters from the String:\n" + "\tparameters = new Parameter (\"PVL.test\", new Parser (parameters_string));"); // Constructor: String Parser parameters = new Parameter ("PVL.test", new Parser (parameters_string)); if (parameters.Warning () != null) { System.out.println (parameters.Warning ().getMessage ()); System.out.println ("*** Aggregate enclosure mismatch expected."); } else System.out.println ("!!! Aggregate enclosure mismatch expected."); System.out.println ("--- parameters.Description ()"); System.out.println (parameters.Description ()); System.out.println ("--- parameters.Description (new Lister ().Strict (true).Indent_Arrays (false))"); System.out.println (parameters.Description (new Lister ().Strict (true).Indent_Arrays (false))); System.out.println ("--- Checking the aggregate list:\n" + "\tIs_Parameter_List (parameters.List ())"); // Is_Parameter_List: List if (! Parameter.Is_Parameter_List (parameters.List ())) { System.out.println ("!!! Does not have a valid aggregate list."); System.exit (1); } else System.out.println ("*** Correct"); System.out.println ("--- Writing the parameters:\n" + "\tparameters.Write ();"); // Write: count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Creating a File for the parameters:\n" + "\tfile = new File (\"/tmp/PVL.test\");"); File file = new File ("/tmp/PVL.test"); // Write: File System.out.println ("--- Writing the parameters to the File:\n" + "\tparameters.Write (new FileOutputStream (file));"); parameters.Write (new FileOutputStream (file)); System.out.println ("--- Constructing a param from the File:\n" + "\tparam = new Parameter (\"File:PVL.test\", new Parser (file));"); // Constructor: File Parser param = new Parameter ("File:PVL.test", new Parser (file)); if (param.Warning () != null) { System.out.println (param.Warning ().getMessage ()); System.out.println ("!!! Aggregate enclosure mismatch not fixed."); } else System.out.println ("*** Aggregate enclosure mismatch fixed."); file.delete (); System.out.println ("--- param.Write ();"); count = param.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Getting \"PVL.test\" from \"File:PVL.test\":\n" + "\tparam = param.Remove (param.Find (\"PVL.test\"));"); // Find: String new_param = param.Find ("PVL.test"); if (new_param == null) { System.out.println ("!!! Find Failed!"); System.exit (1); } // Remove: Parameter param = param.Remove (new_param); if (param == null) { System.out.println ("!!! Remove Failed!"); System.exit (1); } System.out.println ("--- Testing equality of parameters and param:\n" + "\tparameters.equals (param)"); // equals: if (parameters.equals (param)) System.out.println ("*** true"); else System.out.println ("!!! false"); System.out.println ("--- Constructing a param from the parameters:\n" + "\tparam = new Parameter (parameters);"); // Constructor: Parameter copy param = new Parameter (parameters); System.out.println ("--- param.Write ();"); count = param.Write (); System.out.println ("--- parameters.Write ();"); count = parameters.Write (); System.out.println ("--- Testing equivalence of parameters and param:\n" + "\tparameters.equalsIgnoreCase (param)"); // equalsIgnoreCase: if (parameters.equalsIgnoreCase (param)) System.out.println ("*** true"); else System.out.println ("!!! false"); // toString: System.out.println ("--- Iterating through " + parameters); print_params (parameters); System.out.println ("--- Finding \"SUB_A2\":\n" + "\tparam = parameters.Find (\"SUB_A2\");"); // Find: Name param = parameters.Find ("SUB_A2"); if (param == null) { System.out.println ("!!! Not found"); System.exit (1); } // Classification_Name: // Path_Name: System.out.println ("*** Found: " + param.Classification_Name () + " " + param.Path_Name ()); System.out.println ("--- Setting to END_AGGREGATE:\n" + "\tparam_class = param.Classification ();\n" + "\tparam.Classification (Parameter.END_AGGREGATE);"); // Classification: END_AGGREGATE param_class = param.Classification (); param.Classification (Parameter.END_AGGREGATE); System.out.println (param.Classification_Name () + " " + param.Path_Name ()); parameters.Write (); System.out.println ("\n--- Finding \"SUB_A3\":\n" + "\tparameters.Find (\"SUB_A3\")"); // Find: past END_AGGREGATE if (parameters.Find ("SUB_A3") == null) System.out.println ("*** Not found (correct)"); else System.out.println ("!!! Found (shouldn't have)"); // Parent: System.out.println ("--- Iterating through " + param.Parent ().Name ()); print_params (param.Parent ()); System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Restoring the previous classification:\n" + "\tparam.Classification (param_class);"); // Classification: restore list param.Classification (param_class); param.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Finding the previously found parameter:\n" + "\tparam = parameters.Find (param);"); // Find: Parameter param = parameters.Find (param); if (param == null) { System.out.println ("!!! Not found"); System.exit (1); } System.out.println ("*** Found"); System.out.println ("--- Iterating through " + param.Parent ().Name ()); print_params (param.Parent ()); System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Finding \"TOP_1\":\n" + "\tparam = parameters.Find (\"TOP_1\", Parameter.AGGREGATE);"); // Find: Name and Classification param = parameters.Find ("TOP_1", Parameter.AGGREGATE); if (new_param == null) { System.out.println ("!!! Not found"); System.exit (1); } if (! new_param.Is_Aggregate ()) { System.out.println ("!!! Found Assignment: " + param.Path_Name ()); System.exit (1); } System.out.println ("*** Found"); System.out.println ("--- Finding parameter with Value 2.0:\n" + "\tparam = parameters.Find\n" + "\t (\n" + "\t new Parameter ().Value (2.0),\n" + "\t new Selection ()\n" + "\t .Value (true)\n" + "\t .Data (true)\n" + "\t );"); // Value: Real // Selection: Value Data // Find: Value param = parameters.Find ( new Parameter ().Value (2.0), new Selection () .Value (true) .Data (true) ); if (param == null) { System.out.println ("!!! Not found"); System.exit (1); } // Path_to_Name: System.out.print ("*** Found: " + param.Path_to_Name ()); param.Write (); System.out.println ("--- Setting to END_PVL:\n" + "\tparam_class = param.Classification ();\n" + "\tparam.Classification (Parameter.END_PVL);"); // Classification: END_PVL param_class = param.Classification (); param.Classification (Parameter.END_PVL); System.out.println ("--- Iterating through " + param.Parent ().Name ()); print_params (param.Parent ()); System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Constructing a param from the parameters:\n" + "\tparam = new Parameter (parameters);"); // Constructor: Parameter copy param = new Parameter (parameters); System.out.println ("--- param.Write ();"); count = param.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Testing equivalence of parameters and param:\n" + "\tparameters.equalsIgnoreCase (param)"); // equalsIgnoreCase: if (parameters.equalsIgnoreCase (param)) System.out.println ("*** true"); else System.out.println ("!!! false"); System.out.println ("--- Testing equality of parameters and param:\n" + "\tparameters.equals (param)"); // equals: if (parameters.equals (param)) System.out.println ("!!! true; but not identical"); else System.out.println ("*** false; not identical (correct)"); System.out.println ("--- Finding the END_PVL parameter:\n" + "\tparam = parameters.Find (Parameter.END_PVL);"); // Find: Classification param = parameters.Find (Parameter.END_PVL); if (param == null) { System.out.println ("!!! Not found"); System.exit (1); } System.out.println ("*** Found"); System.out.println ("--- Restoring the previous classification:\n" + "\tparam.Classification (param_class);"); // Classification: restore list param.Classification (param_class); System.out.println ("--- Iterating through " + param.Parent ().Name ()); print_params (param.Parent ()); System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Try iterating on an ASSIGNMENT (should show nothing): " + param.Classification_Name () + " " + param); print_params (param); System.out.println ("---"); System.out.println ("--- New TEXT Value parameter:\n" + "\tnew_param = new Parameter (\"New\").Value (\"'This is a new parameter'\");"); // Value: String TEXT new_param = new Parameter ("New").Value ("\"This is a new parameter\""); new_param.Write (); System.out.println ("--- Add it to a token parameter:\n" + "\tparam = new Parameter(\"Test\").Add (new_param);"); // Add: Assignment to Token param = new Parameter("Test").Add (new_param); param.Write (); System.out.println ("--- Change \"Test\" back to a token:\n" + "\tparam.Classification (Parameter.TOKEN);"); // Classification: Aggregate to Token param.Classification (Parameter.TOKEN); param.Write (); System.out.println ("--- Add the \"PVL.test\" parameters to the \"New\" parameter:\n" + "\tnew_param.Add (parameters);"); // Add: Aggregate to Assignment test = true; try {new_param.Add (parameters);} catch (PVL_Exception exception) { System.out.println (exception.getMessage () + "\n"); System.out.println ("*** Correct"); test = false; } if (test) { System.out.println ("!!! Wrong: should have failed."); System.exit (1); } System.out.println ("--- Add the \"New\" parameter to the \"PVL.test\" parameters:\n" + "\tparameters.Add (new_param);"); // Add: Assignment to Aggregate parameters.Add (new_param); print_params (parameters); System.out.println ("--- Remove it:\n" + "\tparameters.Remove (new_param);"); // Remove: Parameter parameters.Remove (new_param); print_params (parameters); System.out.println ("--- Try to Add a Vector containing a String:\n" + "\tparameters.Add (vector);"); Vector vector = new Vector (); vector.add (new String ("A String")); // Add: String Vector to Aggregate test = true; try {parameters.Add (vector);} catch (PVL_Exception exception) { System.out.println (exception.getMessage () + "\n"); System.out.println ("*** Correct"); test = false; } if (test) { System.out.println ("!!! Wrong: should have failed."); System.exit (1); } System.out.println ("--- ListIterator starting at second entry:\n" + "\tlist = parameters.listIterator (1);"); // ListIterator: ListIterator list = parameters.listIterator (1); System.out.println ("--- Iterating forward:\n" + "\twhile (list.hasNext ()) {\n" + "\t list.next ();"); // ListIterator: hasNext while (list.hasNext ()) { // ListIterator: next param = (Parameter)list.next (); System.out.println (param.Classification_Name () + ": " + param.Name ()); if (param.Name ().equals ("SUB_A")) { System.out.println ("--- add \"New\" parameter after \"SUB_A\":\n" + "\tlist.add (new_param);"); // ListIterator: add list.add (new_param); } } System.out.println ("--- Iterating back:\n" + "\twhile (list.hasPrevious ()) {\n" + "\t list.previous ();"); // ListIterator: hasPrevious while (list.hasPrevious ()) { // ListIterator: previous param = (Parameter)list.previous (); System.out.println (param.Classification_Name () + ": " + param.Name ()); } System.out.println ("--- Iterating foward again"); while (list.hasNext ()) { param = (Parameter)list.next (); System.out.println (param.Classification_Name () + ": " + param.Name ()); if (param.Name ().equals ("New")) { System.out.println ("--- remove \"New\" parameter:\n" + "\tlist.remove ();"); // ListIterator: remove list.remove (); } } System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Iterating back again"); while (list.hasPrevious ()) { param = (Parameter)list.previous (); System.out.println (param.Classification_Name () + ": " + param.Name ()); if (param.Name ().equals ("SUB_A")) { System.out.println ("--- Replace \"SUB_A\" with \"New\" parameter:\n" + "\tlist.set (new_param);"); // ListIterator: set list.set (new_param); break; } } System.out.println ("--- Writing everything"); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Insert \"SUB_A\" at the third entry:\n" + "\tparameters.Insert (param:\"SUB_A\", 2);"); // Insert: parameters.Insert (param, 2); count = parameters.Write (); System.out.println (count + " characters written.\n"); System.out.println ("--- Write to a Byte Array:\n" + "\tparameters.Write (ByteArrayOutputStream);"); ByteArrayOutputStream description = new ByteArrayOutputStream (); // Write: to ByteArrayOutputStream count = parameters.Write (description); System.out.println (description); System.out.println (count + " characters written.\n"); } catch (Exception exception) { exception.printStackTrace (System.out); System.out.println ("!!! Fatal flaw: FIX IT\n"); System.exit (-1); } System.exit (0); } public static void print_params (Parameter parameter) { // Iterator: Iterator list = parameter.iterator (); while (list.hasNext ()) { Parameter param = (Parameter)list.next (); System.out.println (param.Classification_Name () + ": " + param.Name ()); } // Test for END_PVL. try {list.next ();} catch (NoSuchElementException exception) { if (exception.getMessage () .startsWith (Parameter.Classification_Name (Parameter.END_PVL))) System.out.println ("~~~ " + exception.getMessage ()); } } } pirl-2.3.8/PIRL/PVL/PVL_Exception.java0000644000175000017500000001754411742734277017126 0ustar mathieumathieu/* PVL_Exception PIRL CVS ID: PVL_Exception.java,v 1.12 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; import java.lang.Exception; /** A PVL_Exception is thrown by classes in the PVL package. To differentiate the possible causes for a PVL_Exception, while avoiding a profusion of subclasses, a local Message String is provided that describes the reason for the exception. The getMessage method of the Exception base class will always return a String that includes this message along with any other description that was provided when the exceptpion was created. In addition, a Location is available which can indicate where a Parser encountered an exception in an input stream.

    @author Bradford Castalia, UA/PIRL @version 1.12 */ public class PVL_Exception extends Exception { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.PVL_Exception (1.12 2012/04/16 06:14:23)"; /** The name of this exception. */ public static final String Exception_Name = "PVL Exception"; // Hard errors: /** Message: Unexpected system error! */ public static final String SYSTEM_ERROR = "Unexpected system error!"; /** Message: Invalid argument. */ public static final String BAD_ARGUMENT = "Invalid argument."; /** Message: Incompatible object class types. */ public static final String INCOMPATIBLE_TYPES = "Incompatible object class types."; /** Message: Problem with file I/O. */ public static final String FILE_IO = "Problem with file I/O."; // Syntax errors: /** Message: Missing end of quoted string. */ public static final String MISSING_QUOTE_END = "Missing end of quoted string."; /** Message: Missing end of comment string. */ public static final String MISSING_COMMENT_END = "Missing end of comment string."; /** Message: Missing end of units string. */ public static final String MISSING_UNITS_END = "Missing end of units string."; /** Message: Reserved character in symbol. */ public static final String RESERVED_CHARACTER = "Reserved character in symbol."; /** Message: Illegal syntax. */ public static final String ILLEGAL_SYNTAX = "Illegal syntax."; /** Message: Empty statement or list. */ public static final String EMPTY_STATEMENT = "Empty statement or list."; /** Message: Numeric value out of range. */ public static final String VALUE_OVERFLOW = "Numeric value out of range."; /** Message: Array enclosure characters do not match. */ public static final String ARRAY_CLOSURE_MISMATCH = "Array enclosure characters do not match."; // Abstract (high level) usage warnings: /** Message: Inappropriate aggregate identifier (not a string). */ public static final String GROUP_VALUE = "Inappropriate aggregate identifier (not a string)."; /** Message: Missing end of aggregate parameters. */ public static final String MISSING_AGGREGATE_END = "Missing end of aggregate parameters."; /** Message: Aggregate enclosure parameters do not match. */ public static final String AGGREGATE_CLOSURE_MISMATCH = "Aggregate enclosure parameters do not match."; /** Message: Binary sized records detected. */ public static final String SIZED_RECORDS = "Binary sized records detected."; /** Message: Could not find the last location. */ public static final String NO_LAST_LOCATION = "Could not find the last location."; private String _Message_ = null; private long _Location_ = -1; /** Creates a PVL_Exception with the PVL_Exception class identification String as the default Message. */ public PVL_Exception () {this (ID);} /** Creates a PVL_Exception with the specified message.

    @param message A String to set as the exception Message. */ public PVL_Exception ( String message ) { super ( Exception_Name + ":\n" + message ); _Message_ = message; } /** Creates a PVL_Exception with the specified message and explanation text. The expanation will follow the message after a newline in the getMessage String.

    @param message A String to set as the exception Message. @param explanation A String to append to the exception description. */ public PVL_Exception ( String message, String explanation ) { this ( message + "\n" + explanation ); _Message_ = message; } /** Creates a PVL_Exception with the specified ID line, message, and explanation text. The ID line will precede the message, and the expanation will follow, in the getMessage String.

    @param ID A String to precede the message in the exception description. @param message A String to set as the exception Message. @param explanation A String to append to the exception description. */ public PVL_Exception ( String ID, String message, String explanation ) { this ( ID + "\n" + message + "\n" + explanation ); _Message_ = message; } /** Creates a PVL_Exception with the specified ID line, message, explanation text, and location value. The ID line will precede the message, and the expanation will follow, in the getMessage String. A line providing the location value will also be appended.

    @param ID A String to precede the message in the exception description. @param message A String to set as the exception Message. @param explanation A String to append to the exception description. @param location A long to set as the exception Location. */ public PVL_Exception ( String ID, String message, String explanation, long location ) { this ( ID + "\n" + message + "\n" + explanation + "\n" + "At data input location " + location + "." ); _Message_ = message; _Location_ = location; } /** Gets a String composed of "PVL Exception: " followed by the Message.

    @return A brief description of the exception. */ public String toString () { return (Exception_Name + ": " + _Message_); } /** Gets the exception's local message String. The message String identifies the specific cause for the exception. It might be used as follows:

    try
        {
        // Some PVL operation.
        }
    catch (PVL_Exception exception)
        {
        if (exception.Message ().equals (PVL_Exception.FILE_IO))
            {
            // Do something with the file.
            }
        // ...
        }
    

    @return The exception's local message String. */ public String Message () {return _Message_;} /** Gets the exception's location value. The value will be -1 if no value was set.

    @return The exception's location value. */ public long Location () {return _Location_;} } // End of class pirl-2.3.8/PIRL/PVL/Selection.java0000644000175000017500000014736111742734277016375 0ustar mathieumathieu/* Selection PIRL CVS ID: Selection.java,v 1.13 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; import java.util.Vector; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.regex.PatternSyntaxException; /** A Selection implements a Selector for use with a Parameter or Value wherever selective comparison of Parameters and/or Values is needed.

    For example, both the Parameter and Value Find methods use a Selector when making object comparisons, and use this Selection class when no other Selector is specified. A Selection can be used to specify the criteria to be used in comparing all, or any part, of two Parameters or Values.

    The criteria are specified as the characteristics of the Parameter and/or Value to use in making a selection, and the combinatorial boolean logic to apply to the characteristics when comparing one object against another. The Selector interface defines the criteria symbols. The Selection class provides the definition of the criteria and comparison methods.

    @author Bradford Castalia, UA/PIRL @version 1.13 */ public class Selection implements Selector { /** Class name and version identification. */ public static final String ID = "PIRL.PVL.Selection (1.13 2012/04/16 06:14:23)"; private int _Criteria_ = ANY | SPECIFIC | EQUAL, _Last_Parameter_Criteria_ = 0, _Last_Value_Criteria_ = 0; private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_PARAMETERS = 1 << 1, DEBUG_NAME = 1 << 2, DEBUG_VALUES = 1 << 3, DEBUG_CLASS = 1 << 4, DEBUG_TYPES = 1 << 5, DEBUG_BASES = 1 << 6, DEBUG_UNITS = 1 << 7, DEBUG_DATA = 1 << 8, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Selection using ANY criteria.

    This Selection will match anything; i.e. any two Parameters or Values will always match with this Selection. */ public Selection () {} /** Creates a Selection using the specified criteria.

    The criteria code may be any combination of Selector criteria.

    @param criteria A Selector criteria code value. @see Selector */ public Selection (int criteria) {_Criteria_ = criteria;} /** Creates a Selection using the criteria from the specified Selector.

    @param selector The Selector from which to get the criteria to use. */ public Selection (Selector selector) {this(selector.Criteria ());} /*------------------------------------------------------------------------------ Accessors */ /** Sets Selection criteria to the specified code.

    @param criteria The Selector criteria to use. @return This Selection. @see Selector */ public Selector Criteria (int criteria) {_Criteria_ = criteria; return this;} /** Gets the current Selection criteria code.

    @return The current Selection criteria code. */ public int Criteria () {return _Criteria_;} /** Gets the current Parameter Selection criteria code.

    Only that portion of the current Selection criteria code for the PARAMETER_SELECTION is returned; all other criteria are masked out.

    @return The PARAMETER_SELECTION section of the current Selection criteria. @see Selector#PARAMETER_SELECTION */ public int Parameter_Criteria () {return _Criteria_ & PARAMETER_SELECTION;} /** Gets the current Value Selection criteria code.

    Only that portion of the current Selection criteria code for the VALUE_SELECTION is returned; all other criteria are masked out.

    @return The VALUE_SELECTION section of the current Selection criteria. @see Selector#VALUE_SELECTION */ public int Value_Criteria () {return _Criteria_ & VALUE_SELECTION;} /** Gets the modifiers of the current Selection criteria code.

    Only that portion of the current Selection criteria code for the MODIFIERS is returned; all other criteria are masked out.

    @return The MODIFIERS section of the current Selection criteria. @see Selector#MODIFIERS */ public int Modifiers () {return _Criteria_ & MODIFIERS;} /** Gets the logic settings of the current Selection criteria code.

    Only that portion of the current Selection criteria code for the LOGIC is returned; all other criteria are masked out.

    @return The LOGIC section of the current Selection criteria. @see Selector#LOGIC */ public int Logic () {return _Criteria_ & LOGIC;} /** Gets a description of the Selection criteria. */ public String toString () { String description = "PIRL.PVL.Selection Criteria " + Integer.toString (_Criteria_, 2); description += NL + " Parameter (" + Integer.toString (Parameter_Criteria (), 2) + ") -"; if (Any_Parameter ()) description += " Any"; if (Comments ()) description += " Comments"; if (Name ()) description += " Name"; if (Classification ()) description += " Classification"; if (Value ()) description += " Value"; description += NL + " Value (" + Integer.toString (Value_Criteria (), 2) + ") -"; if (Any_Value ()) description += " Any"; if (Data ()) description += " Data"; if (Type ()) description += " Type"; if (Base ()) description += " Base"; if (Units ()) description += " Units"; description += NL + " Modifiers (" + Integer.toString (Modifiers (), 2) + ") -"; if (Pattern_Match ()) description += " Pattern"; if (Specific ()) description += " Specific"; description += NL + " Logic (" + Integer.toString (Logic (), 2) + ") -"; if (Equal ()) description += " Equal"; if (Less_Than ()) description += " Less-than"; if (Greater_Than ()) description += " Greater-than"; if (And ()) description += " And"; return description; } /*.............................................................................. Parameter criteria: */ /** Enables or disables the Parameter name criteria.

    @param mode true to enable the use of the Parameter name as a match criteria; false to disable this criteria. @return This Selection. @see Selector#NAME */ public Selector Name (boolean mode) { if (mode) _Criteria_ |= NAME; else _Criteria_ &= ~NAME; return this; } /** Tests if the Parameter name criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Name(boolean) */ public boolean Name () {return ((_Criteria_ & NAME) != 0);} /** Enables or disables the Parameter classification criteria.

    @param mode true to enable the use of the Parameter classification as a match criteria; false to disable this criteria. @return This Selection. @see Selector#CLASSIFICATION */ public Selector Classification (boolean mode) { if (mode) _Criteria_ |= CLASSIFICATION; else _Criteria_ &= ~CLASSIFICATION; return this; } /** Tests if the Parameter classification criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Classification(boolean) */ public boolean Classification () {return ((_Criteria_ & CLASSIFICATION) != 0);} /** Enables or disables the Parameter Value criteria.

    @param mode true to enable the use of the Parameter Value as a match criteria; false to disable this criteria. @return This Selection. @see Selector#VALUE */ public Selector Value (boolean mode) { if (mode) _Criteria_ |= VALUE; else _Criteria_ &= ~VALUE; return this; } /** Tests if the Parameter Value criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Value(boolean) */ public boolean Value () {return ((_Criteria_ & VALUE) != 0);} /** Enables or disables the Parameter comments criteria.

    @param mode true to enable the use of the Parameter comments as a match criteria; false to disable this criteria. @return This Selection. @see Selector#COMMENTS */ public Selector Comments (boolean mode) { if (mode) _Criteria_ |= COMMENTS; else _Criteria_ &= ~COMMENTS; return this; } /** Tests if the Parameter comments criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Comments(boolean) */ public boolean Comments () {return ((_Criteria_ & COMMENTS) != 0);} /** Enables or disables using any Parameter characteristic to match.

    Enabling this mode will disable all Parameter criteria, which is a special case interpreted to mean that any Parameter characteristic matches with those of another Parameter. If, however, AND logic is enabled then the Value matching criteria must still be met. Disabling this mode has no effect; specific Parameter criteria must be enabled once Any_Parameter has been used.

    @param mode true to enable any Parameter characteristics to match; false leaves the current criteria unchanged. @return This Selection. @see Selector#PARAMETER_SELECTION */ public Selector Any_Parameter (boolean mode) { if (mode) _Criteria_ &= ~PARAMETER_SELECTION; return this; } /** Tests if any Parameter characteristic will match.

    @return true if no Parameter criteria are enabled; false otherwise. @see #Any_Parameter(boolean) */ public boolean Any_Parameter () {return ((_Criteria_ & PARAMETER_SELECTION) == ANY || (_Criteria_ & ANY_PARAMETER) != 0);} /*.............................................................................. Value criteria: */ /** Enables or disables using the data of a Value as a criteria.

    @param mode true to enable the use of Value data as a match criteria; false to disable this criteria. @return This Selection. @see Selector#DATA */ public Selector Data (boolean mode) { if (mode) _Criteria_ |= DATA; else _Criteria_ &= ~DATA; return this; } /** Tests if the data of a Value is a criteria.

    @return true if the data of a Value is a criteria; false otherwise. @see #Data(boolean) */ public boolean Data () {return ((_Criteria_ & DATA) != 0);} /** Enables or disables the Value type criteria.

    @param mode true to enable the use of the Value type as a match criteria; false to disable this criteria. @return This Selection. @see Selector#TYPE */ public Selector Type (boolean mode) { if (mode) _Criteria_ |= TYPE; else _Criteria_ &= ~TYPE; return this; } /** Tests if the Value type criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Type(boolean) */ public boolean Type () {return ((_Criteria_ & TYPE) != 0);} /** Enables or disables the Value integer base criteria.

    @param mode true to enable the use of the Value integer base as a match criteria; false to disable this criteria. @return This Selection. @see Selector#BASE */ public Selector Base (boolean mode) { if (mode) _Criteria_ |= BASE; else _Criteria_ &= ~BASE; return this; } /** Tests if the Value integer base criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Base(boolean) */ public boolean Base () {return ((_Criteria_ & BASE) != 0);} /** Enables or disables the Value units description criteria.

    @param mode true to enable the use of the Value units description as a match criteria; false to disable this criteria. @return This Selection. @see Selector#UNITS */ public Selector Units (boolean mode) { if (mode) _Criteria_ |= UNITS; else _Criteria_ &= ~UNITS; return this; } /** Tests if the Value units description criteria is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Units(boolean) */ public boolean Units () {return ((_Criteria_ & UNITS) != 0);} /** Enables or disables using any Value characteristic to match.

    Enabling this mode will disable all Value criteria, which is a special case interpreted to mean that any Value characteristic matches with those of another Value. Disabling this mode has no effect; specific Value criteria must be enabled once Any_Value has been used.

    @param mode true to enable any Value characteristics to match; false leaves the current criteria unchanged. @return This Selection. @see Selector#VALUE_SELECTION */ public Selector Any_Value (boolean mode) { if (mode)_Criteria_ &= ~VALUE_SELECTION; return this; } /** Tests if any Value characteristic will match.

    @return true if no Value criteria are enabled; false otherwise. @see #Any_Value(boolean) */ public boolean Any_Value () {return ((_Criteria_ & VALUE_SELECTION) == ANY || (_Criteria_ & ANY_VALUE) != 0);} /*.............................................................................. Criteria modifiers: */ /** Enables or disables using regular expression pattern matching for String comparisons.

    @param mode true to enable pattern matching; false otherwise. @return This Selection. @see Selector#PATTERN_MATCH */ public Selector Pattern_Match (boolean mode) { if (mode) _Criteria_ |= PATTERN_MATCH; else _Criteria_ &= ~PATTERN_MATCH; return this; } /** Tests if regular expression pattern matching is enabled.

    @return true if the criteria is enabled; false otherwise. @see #Pattern_Match(boolean) */ public boolean Pattern_Match () {return ((_Criteria_ & PATTERN_MATCH) != 0);} /** Enables or disables specific criteria matching.

    When specific matching is enabled Parameter classifications and Value types must match exactly; when disabled only the general categories need to be the same for their criteria to be met. When specific matching is enabled Strings are compared using their equals methods; when disabled their equalsIgnoreCase methods are used. When specific matching is enabled aggregate Parameter lists are compared in their entirerty; when disabled END Parameters are recognized as the end of a list and an END_PVL Parameter marks the end of all nested lists.

    @param mode true to require criteria to match exactly; false will allow criteria to match when the characteristics are generally the same. @return This Selection. @see Selector#SPECIFIC */ public Selector Specific (boolean mode) { if (mode) _Criteria_ |= SPECIFIC; else _Criteria_ &= ~SPECIFIC; return this; } /** Tests if specific criteria matching is enabled.

    @return true if specific criteria matching is enabled; false otherwise. @see #Specific(boolean) */ public boolean Specific () {return ((_Criteria_ & SPECIFIC) != 0);} /*.............................................................................. Criteria logic: */ /** Enables or disables using equality logic for comparing Values with NUMERIC data.

    @param mode true to use equality logic when comparing numeric data values. @return This Selection. @see Selector#SPECIFIC */ public Selector Equal (boolean mode) { if (mode) _Criteria_ |= EQUAL; else _Criteria_ &= ~EQUAL; return this; } /** Tests if equality matching logic is enabled.

    @return true if equality matching logic is enabled; false otherwise. @see #Specific(boolean) */ public boolean Equal () {return ((_Criteria_ & EQUAL) != 0);} /** Enables or disables using less-than logic for comparing Values with NUMERIC data.

    If Equal is also enabled, then <= logic will be used. Note: This method disables Greater_Than logic.

    @param mode true to use less-than logic when comparing numeric data values. @return This Selection. @see Selector#LESS_THAN */ public Selector Less_Than (boolean mode) { if (mode) _Criteria_ |= LESS_THAN; else _Criteria_ &= ~LESS_THAN; Greater_Than (false); return this; } /** Tests if less-than matching logic is enabled.

    @return true if less-than matching logic is enabled; false otherwise. @see #Less_Than(boolean) */ public boolean Less_Than () {return ((_Criteria_ & LESS_THAN) != 0);} /** Enables or disables using greater-than logic for comparing Values with NUMERIC data.

    If Equal is also enabled, then >= logic will be used; however, Less_Than takes precedence if they are both enabled. Note: This method disables Less_Than logic.

    @param mode true to use less-than logic when comparing numeric data values. @return This Selection. @see Selector#LESS_THAN */ public Selector Greater_Than (boolean mode) { if (mode) _Criteria_ |= GREATER_THAN; else _Criteria_ &= ~GREATER_THAN; Less_Than (false); return this; } /** Tests if greater-than matching logic is enabled.

    @return true if greater-than matching logic is enabled; false otherwise. @see #Greater_Than(boolean) */ public boolean Greater_Than () {return ((_Criteria_ & GREATER_THAN) != 0);} /** Enables or disables logical AND when matching with multiple criteria.

    Note: When disabled, logical OR is used when matching with multiple criteria.

    @param mode true to use logical AND when matching with multiple criteria; false to use logical OR. @return This Selection. @see Selector#AND */ public Selector And (boolean mode) { if (mode) _Criteria_ |= AND; else _Criteria_ &= ~AND; return this; } /** Tests if logical AND for multiple criteria is enabled.

    @return true if AND logic is enabled; false if OR logic will be used. @see #And(boolean) */ public boolean And () {return ((_Criteria_ & AND) != 0);} /*------------------------------------------------------------------------------ Parameter criteria match methods: */ /** Tests if two Parameters match.

    If both arguments are null, they match; but if one or the other is null, they do not match.

    When Any_Parameter is enabled, a match occurs. However, if And is also enabled and Any_Value is not enabled, then a match only occurs if both Parameters are the ASSIGNMENT classification and their Values_Match.

    The following criteria are tested in the order listed:

    1. Classification - Classifications_Match
    2. Value - Parameter_Values_Match
    3. Comments - Comments_Match
    4. Name - Names_Match

    When And logic is enabled, all of these criteria tests must match for the Parameters to match (the first one that does not match stops further testing); otherwise any criteria test that matches produces a match for the Parameters (the first one that matches stops further testing).

    N.B.: When the associativity of comparison testing matters - e.g. - the associativity is this_parameter op that_parameter. For example, when a Parameter name is being compared against a regular expression pattern the comparison is this_parameter.Name ().matches (that_parameter.Name ()); i.e. the second Parameter (that_parameter) is expected to contain the pattern to be matched.

    N.B.: Parameter matching of Aggregates is only recursive for Value matching, and in this case only the Values of the Parameter lists are compared.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return true if the Parameters match; false otherwise. @see #And(boolean) @see #Any_Parameter(boolean) @see #Any_Value(boolean) @see Parameter#ASSIGNMENT @see #Values_Match(Value, Value) @see #Classification(boolean) @see #Classifications_Match(Parameter, Parameter) @see #Value(boolean) @see #Parameter_Values_Match(Parameter, Parameter) @see #Comments(boolean) @see #Comments_Match(Parameter, Parameter) @see #Name(boolean) @see #Names_Match(Parameter, Parameter) */ public boolean Parameters_Match ( Parameter this_parameter, Parameter that_parameter ) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (">>> Parameters_Match:\n" + this_parameter + '\n' + that_parameter + '\n' + " criteria = " + Integer.toString (Criteria (), 2) + "\n" + " Name = " + Name () + "\n" + " Classification = " + Classification () + "\n" + " Comments = " + Comments () + "\n" + " Value = " + Value () + "\n" + " Data = " + Data () + "\n" + " Type = " + Type () + "\n" + " Base = " + Base () + "\n" + " Units = " + Units () + "\n" + " Specific = " + Specific () + "\n" + " Pattern_Match = " + Pattern_Match () + "\n" + " AND = " + And () ); boolean matched = false; _Last_Parameter_Criteria_ = 0; if (this_parameter == that_parameter) { _Last_Parameter_Criteria_ = ANY_PARAMETER | NAME | CLASSIFICATION | VALUE | COMMENTS; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Same parameter\n" +"<<< Parameters_Match: true"); return true; } if (this_parameter == null || that_parameter == null) { // A Single null matches nothing. if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" One parameter is null\n" +"<<< Parameters_Match: false"); return false; } if (Any_Parameter ()) { if (And () && ! Any_Value ()) { // A match against a Value is required. if (this_parameter.Is_Assignment () && that_parameter.Is_Assignment ()) { try { matched = Values_Match (this_parameter.Value (), that_parameter.Value ()); } catch (PVL_Exception exception) { // Shouldn't happen since they're both assignments. matched = false; } } else // Non-assignments can't match when a Value is required. matched = false; } else matched = true; _Last_Parameter_Criteria_ |= ANY_PARAMETER; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Any_Parameter = " + matched + '\n' +"<<< Parameters_Match: " + matched); return matched; } if (Classification ()) { matched = Classifications_Match (this_parameter, that_parameter); if (matched) _Last_Parameter_Criteria_ |= CLASSIFICATION; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Classifications_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: true"); return true; // OR: Definate match. } } if (Value ()) { matched = Parameter_Values_Match (this_parameter, that_parameter); if (matched) _Last_Parameter_Criteria_ |= VALUE; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Parameter_Values_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: true"); return true; // OR: Definate match. } } if (Comments ()) { matched = Comments_Match (this_parameter, that_parameter); if (matched) _Last_Parameter_Criteria_ |= COMMENTS; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Comments_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: true"); return true; // OR: Definate match. } } /* Name criteria: The name match is done last so that "simpler" matches may come first for OR logic. In the Parameter.Find method pathname tracking requires name matches to succeed for the entire hierarchy of names that may presented to Find. If a match to another criteria is possible, then that should be given preference to avoid the unnecessary searching down into aggregate parameter lists that may be required for a name match. */ if (Name ()) { matched = Names_Match (this_parameter, that_parameter); if (matched) _Last_Parameter_Criteria_ |= NAME; if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println (" Names_Match = " + matched); } if ((DEBUG & DEBUG_PARAMETERS) != 0) System.out.println ("<<< Parameters_Match: " + matched); return matched; } /** Gets the selection criteria that resulted from the last Parameters_Match comparison of two Parameters.

    The Selector criteria code that is returned flags the criteria that were matched during the last use of the {@link #Parameters_Match(Parameter, Parameter) Parameters_Match} method with this Selection object. If no match occured the return value will be zero.

    @return A Selector criteria code. */ public int Parameters_Match () {return _Last_Parameter_Criteria_;} /** Gets the matching criteria for two Parameters.

    The same matching logic as used in the Parameters_Match method is applied here, except that all criteria are tested. Thus the Selector criteria code that is returned indicates all matching criteria. When the Value criteria is enabled and the Parameters are the ASSIGNMENT classification, then the Values_Criteria_Match method is used to also get the matching criteria for the Parameters' Values. If no criteria match the return value will be zero.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return A Selector criteria code. @see #Parameters_Match(Parameter, Parameter) @see #Values_Criteria_Match(Value, Value) */ public int Parameters_Criteria_Match ( Parameter this_parameter, Parameter that_parameter ) { int selection = 0; if (this_parameter == that_parameter) // Same parameter. return ANY_PARAMETER | NAME | CLASSIFICATION | VALUE | COMMENTS; if (this_parameter == null || that_parameter == null) // A Single null matches nothing. return 0; if (Any_Parameter ()) { selection |= ANY_PARAMETER; if (And () && ! Any_Value ()) { // A match against a Value is required. if (this_parameter.Is_Assignment () && that_parameter.Is_Assignment ()) { try { selection |= Values_Criteria_Match (this_parameter.Value (), that_parameter.Value ()); if (selection != ANY_PARAMETER) selection |= VALUE; } catch (PVL_Exception exception) { // Shouldn't happen since they're both assignments. } } } return selection; } if (Name () && Names_Match (this_parameter, that_parameter)) selection |= NAME; if (Classification () && Classifications_Match (this_parameter, that_parameter)) selection |= CLASSIFICATION; if (Value ()) { if (this_parameter.Is_Assignment () && that_parameter.Is_Assignment ()) { try { int values_selection = Values_Criteria_Match (this_parameter.Value (), that_parameter.Value ()); if (values_selection != 0) selection |= values_selection | VALUE; } catch (PVL_Exception exception) { // Shouldn't happen since they're both assignments. } } // Possible aggregates. else if (Parameter_Values_Match (this_parameter, that_parameter)) selection |= VALUE; } if (Comments () && Comments_Match (this_parameter, that_parameter)) selection |= COMMENTS; return selection; } /** Tests if the names of two Parameters match.

    If Pattern_Matching is enabled, then the Name of this_parameter is compared against the regular expression pattern in the Name of that_parameter. If the regular expression syntax is invalid a normal String comparision is done. If Specific matching is enabled, then the String equals method is used; otherwise the equalsIgnoreCase method is used.

    N.B.: If either Parameter is null the match is false.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return true if the Parameter names match; false otherwise. @see String#matches(String) */ public boolean Names_Match ( Parameter this_parameter, Parameter that_parameter ) { if (this_parameter == null || that_parameter == null) return false; if (this_parameter.Name () == null || that_parameter.Name () == null) return false; try { if (Pattern_Match ()) return this_parameter.Name ().matches (that_parameter.Name ()); } catch (PatternSyntaxException exception) {/* Fall through to regular string matching */} if (Specific ()) return this_parameter.Name ().equals (that_parameter.Name ()); return this_parameter.Name ().equalsIgnoreCase (that_parameter.Name ()); } /** Tests if the classifications of two Parameters match.

    If Specific matching is enabled, then the classifications must be identical to match; otherwise general classifications (i.e. AGGREGATE) will also match.

    N.B.: If either Parameter is null the match is false.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return true if the Parameter classifications match; false otherwise. */ public boolean Classifications_Match ( Parameter this_parameter, Parameter that_parameter ) { if (this_parameter == null || that_parameter == null) return false; if (this_parameter.Classification () == that_parameter.Classification ()) return true; if (! Specific () && ((this_parameter.Is_Begin_Aggregate () && that_parameter.Is_Begin_Aggregate ()) || (this_parameter.Is_End_Aggregate () && that_parameter.Is_End_Aggregate ()))) return true; return false; } /** Tests if the data values of two Parameters match.

    If both Parameters have null data values, they match; but if one or the other is null, they do not match.

    For an ASSIGNMENT, Values_Match determines the match.

    For AGGREGATE Parameters, all of the Parameters in the aggregate lists must match, using the current Selection criteria, for a match to occur. However, if Specific matching is not enabled, then END Parameters are recognized as ending lists, and an END_PVL Parameter will stop any further comparisons for all nested aggregate lists. If the Value criteria applies then the match is recursive for all Aggregate Parameters. TOKEN and UNKNOWN Parameter Values always match when their classifications are the same.

    N.B.: If either Parameter is null the match is false.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return true if the Parameter data values match; false otherwise. @see #Values_Match(Value, Value) @see #Specific(boolean) */ public boolean Parameter_Values_Match ( Parameter this_parameter, Parameter that_parameter ) { boolean end[] = {false}; return parameter_values_match (this_parameter, that_parameter, end); } private boolean parameter_values_match ( Parameter this_parameter, Parameter that_parameter, boolean[] end ) { if (this_parameter == null || that_parameter == null) return false; if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (">>> parameter_values_match:\n" + this_parameter + '\n' + that_parameter); if (this_parameter.Data () == null) { if (that_parameter.Data () == null) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Both null Data\n" +"<<< parameter_values_match: true"); return true; } if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" One null Data\n" +"<<< parameter_values_match: false"); return false; } if (that_parameter.Data () == null) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" One null Data\n" +"<<< parameter_values_match: false"); return false; } boolean matches; if (this_parameter.Is_Assignment () && that_parameter.Is_Assignment ()) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" ASSIGNMENT"); try { // N.B.: Values_Match sets _Last_Value_Criteria_. matches = Values_Match (this_parameter.Value (), that_parameter.Value ()); if ((DEBUG & DEBUG_VALUES) != 0) System.out.println ("<<< parameter_values_match: " + matches); return matches; } catch (PVL_Exception exception) { // Shouldn't happen since they're both assignments. return false; } } if (this_parameter.Is_Aggregate () && that_parameter.Is_Aggregate ()) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" AGGREGATE"); matches = true; Iterator these_parameters, those_parameters; if (Specific ()) { // Compare the entire lists. if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Specific"); try { if (this_parameter.List_Size () != that_parameter.List_Size ()) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Different list sizes\n" +"<<< parameter_values_match: false"); return false; } Vector this_list = this_parameter.List (), that_list = that_parameter.List (); if (this_list == null && that_list == null) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Empty lists\n" +"<<< parameter_values_match: true"); return true; } these_parameters = this_list.iterator (); those_parameters = that_list.iterator (); } catch (PVL_Exception exception) { // Shouldn't happen since they're both valid aggregates. return false; } } else { /* END_XXX parameters will end the lists. END_PVL parameters will be specially noticed and the end flag will be set so that no further comparisons will be done up the recursion line. */ if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" ! Specific"); these_parameters = this_parameter.iterator (); those_parameters = that_parameter.iterator (); } while (these_parameters.hasNext () && those_parameters.hasNext ()) { if (! parameter_values_match ( (Parameter)these_parameters.next (), (Parameter)those_parameters.next (), end)) { matches = false; break; } if (end[0]) { // END_PVL in sub-parameter. if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" parameter_values_match: END"); break; } } if (! end[0] && ! Specific ()) { if (these_parameters.hasNext () || those_parameters.hasNext ()) // The lists are not the same size. matches = false; // Test for END_PVL in either list. try { these_parameters.next (); those_parameters.next (); } catch (NoSuchElementException exception) { if (exception.getMessage ().startsWith (Parameter.Classification_Name (Parameter.END_PVL) + "\n")) { // END_PVL in list. if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" parameter_values_match: END_PVL"); end[0] = true; } } } if ((DEBUG & DEBUG_VALUES) != 0) System.out.println ("<<< Selection.parameter_values_match: " + matches); return matches; } if (this_parameter.Classification () == that_parameter.Classification ()) matches = true; else matches = false; if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Classification matches = " + matches + '\n' +"<<< parameter_values_match: " + matches); return matches; } /** Tests if the comments of two Parameters match.

    If both Parameters do not have comments, they match; but if one or the other does not have comments, they do not match. If Specific matching is enabled, then the String equals method is used; otherwise the equalsIgnoreCase method is used.

    N.B.: If either Parameter is null the match is false.

    @param this_parameter The first of a pair of Parameters to be matched. @param that_parameter The second of a pair of Parameters to be matched. @return true if the Parameter comments match; false otherwise. */ public boolean Comments_Match ( Parameter this_parameter, Parameter that_parameter ) { if (this_parameter == null || that_parameter == null) return false; if (this_parameter.Comments () == null) { if (that_parameter.Comments () == null) return true; return false; } if (that_parameter.Comments () == null) return false; if (Specific ()) return this_parameter.Comments ().equals (that_parameter.Comments ()); return this_parameter.Comments ().equalsIgnoreCase (that_parameter.Comments ()); } /*------------------------------------------------------------------------------ Value criteria match methods: */ /** Tests if two Values match.

    When Any_Value is enabled, a match always occurs.

    If both arguments are null, they match; but if one or the other is null, they do not match.

    The following criteria are tested in the order listed:

    1. Type - Types_Match
    2. Base - Bases_Match
    3. Units - Units_Match
    4. Data - Data_Match

    When And logic is enabled, all of the enabled criteria tests must match for the Values to match (the first one that does not match stops further testing); otherwise any criteria test that matches produces a match for the Values (the first one that matches stops further testing).

    N.B.: Value matching of Arrays is only recursive for Data matching, and in this case only the data values of the Array elements are compared.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return true if the Values match; false otherwise. @see #And(boolean) @see #Type(boolean) @see #Types_Match(Value, Value) @see #Base(boolean) @see #Bases_Match(Value, Value) @see #Units(boolean) @see #Units_Match(Value, Value) @see #Data(boolean) @see #Data_Match(Value, Value) */ public boolean Values_Match ( Value this_value, Value that_value ) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (">>> Values_Match:\n" + " criteria = " + Integer.toString (Criteria (), 2) + "\n" + " Data = " + Data () + "\n" + " Type = " + Type () + "\n" + " Base = " + Base () + "\n" + " Units = " + Units () + "\n" + " Specific = " + Specific () + "\n" + " AND = " + And () ); _Last_Value_Criteria_ = 0; if (Any_Value ()) { _Last_Value_Criteria_ = ANY_VALUE; if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Any_Value = true" + '\n' +"<<< Values_Match: true"); return true; } if (this_value == that_value) { // The same Values. _Last_Value_Criteria_ = ANY_VALUE | DATA | TYPE | BASE | UNITS; if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" Same value\n" +"<<< Values_Match: true"); return true; } if (this_value == null || that_value == null) { if ((DEBUG & DEBUG_VALUES) != 0) System.out.println (" One value is null\n" +"<<< Values_Match: false"); return false; } boolean matched = false; if (Type ()) { matched = Types_Match (this_value, that_value); if (matched) _Last_Value_Criteria_ |= TYPE; if ((DEBUG & (DEBUG_VALUES | DEBUG_TYPES)) != 0) System.out.println (" Types_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_TYPES)) != 0) System.out.println ("<<< Values_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_TYPES)) != 0) System.out.println ("<<< Values_Match: true"); return true; // OR: Definate match. } } if (Base ()) { matched = Bases_Match (this_value, that_value); if (matched) _Last_Value_Criteria_ |= BASE; if ((DEBUG & (DEBUG_VALUES | DEBUG_BASES)) != 0) System.out.println (" Bases_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_BASES)) != 0) System.out.println ("<<< Values_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_BASES)) != 0) System.out.println ("<<< Values_Match: true"); return true; // OR: Definate match. } } if (Units ()) { matched = Units_Match (this_value, that_value); if (matched) _Last_Value_Criteria_ |= UNITS; if ((DEBUG & (DEBUG_VALUES | DEBUG_UNITS)) != 0) System.out.println (" Units_Match = " + matched); if (And ()) { if (! matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_UNITS)) != 0) System.out.println ("<<< Values_Match: false"); return false; // AND: Definate no match. } } else if (matched) { if ((DEBUG & (DEBUG_VALUES | DEBUG_UNITS)) != 0) System.out.println ("<<< Values_Match: true"); return true; // OR: Definate match. } } if (Data ()) { matched = Data_Match (this_value, that_value); if (matched) // Data_Match may match other criteria. _Last_Value_Criteria_ |= DATA; if ((DEBUG & (DEBUG_VALUES | DEBUG_DATA)) != 0) System.out.println (" Data_Match = " + matched); } if ((DEBUG & DEBUG_VALUES) != 0) System.out.println ("<<< Values_Match: " + matched); return matched; } /** Gets the selection criteria that resulted from the last Values_Match comparison of two Values.

    The Selector criteria code that is returned flags the criteria that were matched during the last use of the {@link #Values_Match(Value, Value) Values_Match} method with this Selection object. If no match occured the return value will be zero.

    @return A Selector criteria code. */ public int Values_Match () {return _Last_Value_Criteria_;} /** Gets the matching criteria for two Values.

    The same matching logic as used in the Values_Match method is applied here, except that all criteria are tested. Thus the Selector criteria code that is returned indicates all matching criteria. If no criteria match the return value will be zero.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return A Selector criteria code. @see #Values_Match(Value, Value) */ public int Values_Criteria_Match ( Value this_value, Value that_value ) { int selection = 0; if (Any_Value ()) return ANY_VALUE; if (this_value == that_value) // Same value. return ANY_VALUE | DATA | TYPE | BASE | UNITS; if (this_value == null || that_value == null) // A Single null matches nothing. return 0; if (Type () && Types_Match (this_value, that_value)) selection |= TYPE; if (Base () && Bases_Match (this_value, that_value)) selection |= BASE; if (Units () && Units_Match (this_value, that_value)) selection |= UNITS; if (Data () && Data_Match (this_value, that_value)) selection |= DATA; return selection; } /** Tests if the types of two Values match.

    If Specific matching is enabled, then the types must be identical to match; otherwise gneral classifications (i.e. NUMERIC,STRING, or ARRAY) will also match.

    N.B.: If either Value is null the match is false.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return true if the Value types match; false otherwise. */ public boolean Types_Match ( Value this_value, Value that_value ) { if (this_value == null || that_value == null) return false; if (this_value.Type () == that_value.Type ()) return true; if (! Specific () && ((this_value.Is_Numeric () && that_value.Is_Numeric ()) || (this_value.Is_String () && that_value.Is_String ()) || (this_value.Is_Array () && that_value.Is_Array ()))) return true; return false; } /** Tests if the integer radix base values of two Values match.

    When the two Values do not have INTEGER data, a match always occurs. Otherwise the radix base values are compared numerically using the Equal and/or Less_Than or Greater_Than logic of this Selection.

    N.B.: If either Value is null the match is false.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return true if the Value radix base values match; false otherwise. @see #Equal(boolean) @see #Less_Than(boolean) @see #Greater_Than(boolean) */ public boolean Bases_Match ( Value this_value, Value that_value ) { if (this_value == null || that_value == null) return false; if (this_value.Is_Integer () && that_value.Is_Integer ()) { int this_number = this_value.Base (), that_number = that_value.Base (); if (Equal ()) { if (Less_Than ()) return (this_number <= that_number); else if (Greater_Than ()) return (this_number >= that_number); else return (this_number == that_number); } else if (Less_Than ()) return (this_number < that_number); else if (Greater_Than ()) return (this_number > that_number); else return (this_number != that_number); } return true; } /** Tests if the units descriptions of two Values match.

    If both Values do not have units descriptions, they match; but if one or the other does not have a units description, they do not match. If Pattern_Matching is enabled, the the units String of this_value is matched against the regular expression pattern of that_value's units String. If the regular expression syntax is invalid a normal String comparision is done. If Specific matching is enabled, then the String equals method is used; otherwise the equalsIgnoreCase method is used.

    N.B.: If either Value is null the match is false.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return true if the Value units descriptions match; false otherwise. @see String#matches(String) */ public boolean Units_Match ( Value this_value, Value that_value ) { if (this_value == null || that_value == null) return false; String these_units = this_value.Units (), those_units = that_value.Units (); if (these_units != null) { if (those_units != null) { try { if (Pattern_Match ()) return these_units.matches (those_units); } catch (PatternSyntaxException exception) {/* Fall through to regular string matching */} if (Specific ()) return these_units.equals (those_units); return these_units.equalsIgnoreCase (those_units); } } else if (those_units == null) return true; return false; } /** Tests if the data of two Values match.

    If both Values have null data, they match; but if one or the other is null, they do not match.

    If the Values are NUMERIC type the data are compared numerically using the Equal and/or Less_Than or Greater_Than logic of this Selection.

    For STRING types, if Pattern_Matching is enabled the String data of this_value is matched against the regular expression pattern of that_value's String data. If the regular expression syntax is invalid a normal String comparision is done. If Specific matching is enabled, then the String equals method is used; otherwise the equalsIgnoreCase method is used.

    For ARRAY types the data of all Values in the data Vector must match, using the current Selection criteria, for a match to occur. If the Data criteria applies then the match is recursive for all Array Values.

    When both Values are of UNKNONW type, they match.

    N.B.: If either Value is null the match is false.

    @param this_value The first of a pair of Values to be matched. @param that_value The second of a pair of Values to be matched. @return true if the data of the Values match; false otherwise. @see String#matches(String) */ public boolean Data_Match ( Value this_value, Value that_value ) { if (this_value == null || that_value == null) return false; if ((DEBUG & DEBUG_DATA) != 0) System.out.println (">>> Data_Match:\n" + this_value + '\n' + that_value); if (this_value.Data () == null) { if (that_value.Data () == null) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Both null Data\n" +"<<< Data_Match: true"); return true; } if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" One null Data\n" +"<<< Data_Match: false"); return false; } if (that_value.Data () == null) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" One null Data\n" +"<<< Data_Match: false"); return false; } boolean matches; if (this_value.Is_Numeric () && that_value.Is_Numeric ()) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" NUMERIC"); double this_double, that_double; try { this_double = this_value.double_Data (); that_double = that_value.double_Data (); } catch (PVL_Exception exception) {return false; /* Shouldn't happen */} if (Equal ()) { if (Less_Than ()) matches = (this_double <= that_double); else if (Greater_Than ()) matches = (this_double >= that_double); else matches = (this_double == that_double); } else if (Less_Than ()) matches = (this_double < that_double); else if (Greater_Than ()) matches = (this_double > that_double); else matches = (this_double != that_double); if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Data_Match: " + matches); return matches; } if (this_value.Is_String () && that_value.Is_String ()) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" STRING"); try { try { if (Pattern_Match ()) matches = this_value.String_Data ().matches (that_value.String_Data ()); } catch (PatternSyntaxException exception) {/* Fall through to regular string matching */} if (Specific ()) matches = this_value.String_Data ().equals (that_value.String_Data ()); else matches = this_value.String_Data ().equalsIgnoreCase (that_value.String_Data ()); } catch (PVL_Exception exception) {return false; /* Shouldn't happen */} if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Data_Match: " + matches); return matches; } if (this_value.Is_Array () && that_value.Is_Array ()) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" ARRAY"); try { if (this_value.Vector_Data ().size () != that_value.Vector_Data ().size ()) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Different vector sizes\n" +"<<< Data_Match: false"); return false; } } catch (PVL_Exception exception) { // Shouldn't happen since they're both valid arrays. return false; } Iterator these_values = this_value.iterator (), those_values = that_value.iterator (); while (these_values.hasNext () && those_values.hasNext ()) { if (! Data_Match ( (Value)these_values.next (), (Value)those_values.next () )) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Data_Match: false"); return false; } } if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Data_Match: true"); return true; } if (this_value.Is_Unknown () && that_value.Is_Unknown ()) matches = true; else matches = false; if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Both Unknown\n" +"<<< Data_Match: true"); return matches; } } // End of class pirl-2.3.8/PIRL/PVL/PVL_to_DB.java0000644000175000017500000031566511742734277016164 0ustar mathieumathieu/* PVL_to_DB PIRL CVS ID: PVL_to_DB.java,v 1.39 2012/04/16 06:14:23 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; // PIRL packages import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Database.*; import PIRL.Conductor.*; import PIRL.Strings.Words; import PIRL.Strings.String_Buffer; import PIRL.Strings.String_Utilities; import PIRL.Utilities.Host; import PIRL.Utilities.Streams; // Other packages import java.util.Vector; import java.util.Iterator; import java.io.File; import java.io.InputStream; import java.io.StringWriter; import java.text.ParseException; import java.lang.IllegalArgumentException; import java.lang.NumberFormatException; /** PVL_to_DB maps a source of parameter values to database table field values.

    A Map is used to define the assignment of PVL Parameter Values, or constant values, to Database fields. The map is defined using PVL Parameters. The Map defines the assignment from the value of a resolved source parameter reference to a resolved database field reference where the value is deposited. PVL_to_DB binds a PVL source of Parameter Values and a Database target to the Map. In addition, a set of configuration parameters may be specified that will supplement every PVL source mapped to the Database; these may be thought of as default parameters.

    The name of each Assignment Parameter in the map specifies a database reference to a single database field. The database reference may specify either an update of an existing record or an insert of a new record. A database reference may be flagged to try an update and, if this fails, to fallback to an insert. Field reference Assignment Parameters may be contained in an Aggregate Parameter for a single update or insert operation on the Database of the collected assignments.

    The value of each Parameter in the map specifies a parameter source reference. This is resolved against the PVL source, unless it is a constant value. A constant value may be a quoted string or the result of a mathematical expression that evaluates to a constant.

    Both database references and parameter source references may contain nested references of either type, which themselves may contain nested references.

    The map is processed in the order in which the parameters occur. The map may be processed in a loop until either no source references are resolved or a specified number of passes have been accomplished.

    @author Bradford Castalia, Christian Schaller - UA/PIRL @version 1.39 @see PIRL.PVL.Parameter @see PIRL.PVL.Value @see PIRL.Database.Database @see PIRL.Conductor.Reference_Resolver */ public class PVL_to_DB extends Update_DB { /** The Class identification. */ public static final String ID = "PIRL.PVL.PVL_to_DB (1.39 2012/04/16 06:14:23)"; // Configuration: /** The Parameter Group for this class: "PVL_to_DB".

    This group will be extracted from the Configuration parameters, or an empty group of this name will be created if it is not present in the Configuration. Then a set of common parameters are added. It is appended to the PVL source parameters. */ public static final String PVL_to_DB_GROUP = "PVL_to_DB"; private Configuration PVL_to_DB_Group = null; /** The name given to the {@link #Source_Parameters() Source_Parameters} when the Aggregate is based on an Assignment Parameter: "Source". */ public static final String DEFAULT_SOURCE_PARAMETERS_NAME = "Source"; /** The name of the Parameter in the PVL_to_DB group that has the system hostname: "Hostname". */ public static final String HOSTNAME_PARAMETER = "Hostname"; /** The name of the Parameter in the PVL_to_DB group that has the current working directory pathname: "CWD". */ public static final String CWD_PARAMETER = "CWD"; /** The name of the Parameter in the PVL_to_DB group that has the name of the current {@link #Source_Parameters() Source_Parameters}: "Source_Name". */ public static final String SOURCE_NAME_PARAMETER = "Source_Name"; /** The name of the Parameter in the PVL_to_DB group that has the absolute pathname of the current {@link #PVL_Source() PVL_Source} name: {@link Conductor#SOURCE_PATHNAME_PARAMETER SOURCE_PATHNAME_PARAMETER}. */ public static final String SOURCE_PATHNAME_PARAMETER = Conductor.SOURCE_PATHNAME_PARAMETER; /** The name of the Parameter in the PVL_to_DB group that has the directory pathname of the current {@link #PVL_Source() PVL_Source} name: {@link Conductor#SOURCE_DIRECTORY_PARAMETER SOURCE_DIRECTORY_PARAMETER}. */ public static final String SOURCE_DIRECTORY_PARAMETER = Conductor.SOURCE_DIRECTORY_PARAMETER; /** The name of the Parameter in the PVL_to_DB group that has the filename of the current {@link #PVL_Source() PVL_Source} name: {@link Conductor#SOURCE_FILENAME_PARAMETER SOURCE_FILENAME_PARAMETER}. */ public static final String SOURCE_FILENAME_PARAMETER = Conductor.SOURCE_FILENAME_PARAMETER; /** The name of the Parameter in the PVL_to_DB group that has the portion of the {@link #SOURCE_FILENAME_PARAMETER SOURCE_FILENAME_PARAMETER} value without the extension (the portion preceding the last period character): {@link Conductor#SOURCE_FILENAME_ROOT_PARAMETER SOURCE_FILENAME_ROOT_PARAMETER}. */ public static final String SOURCE_FILENAME_ROOT_PARAMETER = Conductor.SOURCE_FILENAME_ROOT_PARAMETER; /** The name of the Parameter in the PVL_to_DB group that has the portion of the {@link #SOURCE_FILENAME_PARAMETER SOURCE_FILENAME_PARAMETER} value following the last period ('.') character: {@link Conductor#SOURCE_FILENAME_PARAMETER SOURCE_FILENAME_PARAMETER}. */ public static final String SOURCE_FILENAME_EXTENSION_PARAMETER = Conductor.SOURCE_FILENAME_EXTENSION_PARAMETER; /** The name of the Parameter in the PVL_to_DB group that has the current mapping loop counter: "Loop_Counter".

    The value of this parameter is incremented for each mapping {@link #Loop(boolean) Loop} through the {@link #PVL_Source() Source PVL} parameters. It is initialized to 0. */ public static final String LOOP_COUNTER_PARAMETER = "Loop_Counter"; /** The {@link #Loop(boolean) Loop} limit: 256.

    The maximum number of times that a mapping loop is allowed to occur. */ public int Loop_Limit = 256; private boolean Loop = false; // Map: /** The Group name of the map parameters: "PVL_to_DB_Map". */ public static final String MAP_NAME = "PVL_to_DB_Map"; private Parameter The_Map = null; // Reference_Resolver private Reference_Resolver The_Resolver = null; /** Default value if a reference can not be resolved. */ public static String RESOLVER_DEFAULT_VALUE = null; // Source Parameters: private Parameter Source_Parameters = null; /** Whether to retry failed resolving of Group name nested references with both configuration and source parameters. */ public boolean Retry_Group_with_Source = true; /** Alternative source parameter reference token character. */ public static final char ALTERNATIVE_MARKER = '|'; /** Non-math expression source parameter reference token character. */ public static final char NO_MATH_MARKER = ':'; /** Update-or-insert database reference marker character.

    A database reference prefixed with this marker will be used for an insert operation if the initial update operation fails. If the database reference does not include a record key specification the marker will simply be ignored. N.B.: The marker is not part of the database reference; it is removed before the database reference is used. */ public static final char UPDATE_OR_INSERT_MARKER = '*'; /** Whether an unresolved source parameter reference will throw an Invalid_Map_Syntax exception by default. */ public static boolean UNRESOLVED_SOURCE_REFERENCE_THROWS = false; /** The Configuration parameter which, if {@link Configuration#Enabled(String, boolean) enabled}, will cause any unresolved source parameter reference to throw an Invalid_Map_Syntax exception.

    If this parameter is not found, the {@link #UNRESOLVED_SOURCE_REFERENCE_THROWS} value will be used by default. */ public static final String UNRESOLVED_SOURCE_REFERENCE_PARAMETER = "Unresolved_Source_Reference_Throws"; private boolean Unresolved_Source_Reference_Throws = UNRESOLVED_SOURCE_REFERENCE_THROWS; // No database operations. private boolean No_Op = false; /** Exit status: Success. */ public static final int EXIT_SUCCESS = 0; /** Exit status: PVL processing problem. */ public static final int EXIT_PVL_ERROR = 1; /** Exit status: Invalid syntax was found in the map. */ public static final int EXIT_INVALILD_SYNTAX = 2; /** Exit status: An unresolved reference was encountered. */ public static final int EXIT_UNRESOLVED_REFERENCE = 3; // Debug control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_CONFIGURATION = 1 << 1, DEBUG_MAP = 1 << 2, DEBUG_SOURCE = 1 << 3, DEBUG_PARAMETER_REFERENCE = 1 << 4, DEBUG_FIELD_REFERENCE = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs from a Database and a map source.

    @param database The Database to be target. @param map_source The source of the database map. May be a URL refering to an appropriate database map. @throws Configuration_Exception If there is a problem with the configuration file. @throws Database_Exception If there is a problem connecting with the database. @throws PVL_Exception If there is a problem parsing the PVL from the database map file. */ public PVL_to_DB ( Database database, String map_source ) throws Configuration_Exception, PVL_Exception {this (database, Assemble_PVL (map_source));} /** Constructs from a Database and a Parameter map Aggregate.

    The {@link Update_DB#Update_One_Only(boolean) update mode} is set to only allow one record per update operation.

    @param database The Database to be updated. @param map The Parameter map aggregate. @throws Configuration_Exception If there is a problem with the Database configuration. */ public PVL_to_DB ( Database database, Parameter map ) throws Configuration_Exception { // Construct the base Update_DB object. super (database); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> PVL_to_DB:"); // Default update mode. Update_One_Only (true); // Construct the Reference_Resolver for the database. The_Resolver = new Reference_Resolver (database); // Assign the PVL map. The_Map = map; // Update the configuration. Configure (); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< PVL_to_DB"); } /*============================================================================== Modes */ /** Enables or disables database update operations.

    As an aid in debugging complex references in map parameter syntax final database update operations may be disabled so that the results of resolving map parameter references may be viewed without affecting the database contents. This mode is implicitly {@link Update_DB#Verbose(boolean) verbose}.

    Note: No-op mode is disabled by default.

    @param enable true if no-op mode is to be enabled (database update operations are disabled); false otherwise. @return This PVL_to_DB object. */ public PVL_to_DB No_Op ( boolean enable ) { No_Op = enable; return this; } /** Tests if no-op mode is enabled.

    @return true if no-op mode is enabled; false otherwise. @see #No_Op(boolean) */ public boolean No_Op () {return No_Op;} /** Enables or disables map looping.

    When enabled a (@link #PVL_Source() PVL_Source} is repeatedly mapped until references are exhausted or the {@link #Loop_Limit} is reached.

    Map looping is used in conjuction with the {@link #LOOP_COUNTER_PARAMETER} in the default {@link #PVL_to_DB_GROUP} parameters. The value of the loop counter parameter is initialized to 0 when {@link #PVL_Source() PVL_Source} mapping begins, and then incremented at the end of each mapping loop. By using the LOOP_COUNTER_PARAMETER in the {@link #Map(String) Map} a sequence of parameters from the PVL source can be mapped to the Database. For example, the Map entry:

    	Group = catalog.table
    	  field_1 = parameter@${PVL_to_DB/Loop_Counter}
    	  field_2 = array[${PVL_to_DB/Loop_Counter}]
    	  ...
    	End_Group
    	

    will insert a record in catalog.table with field_1 set to the value of the Nth parameter of that name and field_2 set to the Nth value of the array parameter, where N is the current value of the loop counter (starting with 0).

    Note: Map looping is disabled by default.

    @param enable true if map looping is to be enabled; false otherwise. @return This PVL_to_DB object. */ public PVL_to_DB Loop ( boolean enable ) { Loop = enable; return this; } /** Tests if map looping is enabled.

    @return true if map looping is enabled; false otherwise. @see #Loop(boolean) */ public boolean Loop () {return Loop;} /*============================================================================== Configuration */ /** Update the configuration.

    A {@link #PVL_to_DB_GROUP PVL_to_DB} Group, if not present, is added to the Configuration of the Database. In this parameter group the {@link #HOSTNAME_PARAMETER} is set to the name of the host system, and the {@link #LOOP_COUNTER_PARAMETER} is initialized to 0.

    If the {@link Conductor#UNRESOLVED_REFERENCE_PARAMETER} is present in the Database Configuration, its value is used to set the {@link Reference_Resolver#Default_Value(String) default value} for unresolved references. Otherwise the {@link #RESOLVER_DEFAULT_VALUE} is used.

    @throws Configuration_Exception If there is a problem accessing the configuration. */ private void Configure () throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (">>> PVL_to_DB.Configure"); Configuration configuration = The_Database.Configuration (); if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" The_Database.Configuration -" + NL + configuration.Description () + NL); // Default value for an unresolved nested reference. String default_value = configuration.Get_One (Conductor.UNRESOLVED_REFERENCE_PARAMETER); if (default_value == null) default_value = RESOLVER_DEFAULT_VALUE; else if (default_value.toUpperCase ().startsWith (Conductor.UNRESOLVED_REFERENCE_THROWS)) default_value = null; Default_Value (default_value); // Throw an exception on an unresolved source reference. Unresolved_Source_Reference_Throws = configuration.Enabled (UNRESOLVED_SOURCE_REFERENCE_PARAMETER, UNRESOLVED_SOURCE_REFERENCE_THROWS); /* Initialize the PVL_to_DB_Group adding any PVL_to_DB group parameters from the Database Configuration. */ Default_Parameters (PVL_to_DB_Group (configuration)); if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Effective configuration -" + NL + configuration.Description () + NL +"<<< PVL_to_DB.Configure"); } /*------------------------------------------------------------------------------ Reference Resolver */ /** Sets the {@link Reference_Resolver#Match_Mode(int) match mode} used to resolve both nested configuration parameter references and {@link #Map(String) Map} parameter source references from the {@link #PVL_Source() PVL source}.

    @param match_mode One of the {@link Reference_Resolver#CASE_SENSITIVE}, {@link Reference_Resolver#CASE_INSENSITIVE}, or {@link Reference_Resolver#PATTERN_MATCH} values. @return This PVL_to_DB object. @throws IllegalArgumentException If the match mode is not a valid value. */ public PVL_to_DB Match_Mode ( int match_mode ) throws IllegalArgumentException { The_Resolver.Match_Mode (match_mode); return this; } /** Gets the parameter match mode.

    @return The current parameter match mode code. @see #Match_Mode(int) */ public int Match_Mode () {return The_Resolver.Match_Mode ();} /** Sets the default value for unresolved nested references.

    Note: The default value may be set by using an {@link Conductor#UNRESOLVED_REFERENCE_PARAMETER Unresolved_Reference} parameter in the Configuration of the Database provided when the PVL_to_DB object is constructed. If the value of this parameter starts with {@link Conductor#UNRESOLVED_REFERENCE_THROWS "THROW"} (case insensitive) then unresolved nested references will throw an Unresolved_Reference exception.

    N.B.: The default value only applies to unresolved nested configuration parameter references. Source parameter (non-nested) references never have a default value.

    @param default_value The default String value for unresolved nested references. If null, unresolved references will throw an Unresolved_Reference expception. @return This PVL_to_DB object. @see Reference_Resolver#Default_Value(String) */ public PVL_to_DB Default_Value ( String default_value ) { The_Resolver.Default_Value (default_value); return this; } /** Gets the default that will be used when a nested configuration parameter reference can not be resolved.

    @return The String to use as the default. This is null if Unresolved_Reference will be thrown when a reference can not be resolved. @see #Default_Value(String) */ public String Default_Value () {return The_Resolver.Default_Value ();} /** Force an unresolved source reference to throw an Unresolved_Reference exception.

    Unless the {@link #Configure() configuration} {@link #UNRESOLVED_SOURCE_REFERENCE_PARAMETER} or the {@link #UNRESOLVED_SOURCE_REFERENCE_THROWS} default value is true, a source reference that is not {@link #PVL_Source() resolved} will result in the corresponding database field being ignored. Otherwise an unresolved source reference will result in an Unresolved_Reference exception being thrown.

    N.B.: If {@link #No_Op(boolean) no-op} mode is enabled an exception will not be thrown in any case.

    @param enabled If true an unresolved source reference will result in an Unresolved_Reference exception being thrown. If false an unresolved source reference will result in the mapped database field being ignored. @return This PVL_to_DB object. */ public PVL_to_DB Unresolved_Source_Reference_Throws ( boolean enabled ) { Unresolved_Source_Reference_Throws = enabled; return this; } /** Test whether an unresolved source reference will throw an Unresolved_Reference exception.

    @return true if an unresolved source reference will result in an Unresolved_Reference exception being thrown. false if an unresolved source reference will result in the mapped database field being ignored. @see #Unresolved_Source_Reference_Throws(boolean) */ public boolean Unresolved_Source_Reference_Throws () {return Unresolved_Source_Reference_Throws;} /** Gets the Reference_Resolver being used to resolve all references.

    Warning: Modifying the Reference_Resolver could produce unexpected source mapping results. @return The Reference_Resolver being used. */ public Reference_Resolver Resolver () {return The_Resolver;} /*============================================================================== Source Parameters */ /** Gets the current set of source parameters.

    @return The Aggregate Parameter containing the parameters for resolving source parameter references. N.B.: After {@link #PVL_Source() source mapping} the source parameters include the {@link #Default_Parameters() default parameters} in a {@link #PVL_to_DB_GROUP} Aggregate. @see #Source_Parameters(Parameter) */ public Parameter Source_Parameters () {return Source_Parameters;} /** Sets the source parameter(s) to be mapped.

    If the source is an Assignment a new Aggregate named {@link #DEFAULT_SOURCE_PARAMETERS_NAME} is created to contain it.

    The {@link #Default_Parameters(Parameter) default parameters} are updated: {@link #SOURCE_NAME_PARAMETER} is set to the name of the source Aggregate and {@link #SOURCE_PATHNAME_PARAMETER}, {@link #SOURCE_DIRECTORY_PARAMETER}, {@link #SOURCE_FILENAME_PARAMETER}, {@link #SOURCE_FILENAME_ROOT_PARAMETER}, and {@link #SOURCE_FILENAME_EXTENSION_PARAMETER} are set to the corresponding components of the source name taken to be a file pathname. If, however, the name does not refer to an existing file, then these parameters will all be given the empty string as their value.

    @param source The Parameter source. If null, the current source is unchanged. @return This PVL_to_DB object. @throws Configuration_Exception If there was a problem updating the default parameters from the source name. @see #Default_Parameters() @see Reference_Resolver#Parameters(Parameter) */ public PVL_to_DB Source_Parameters ( Parameter source ) throws Configuration_Exception { if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println (">-< PLV_to_DB.Source_Parameters (Parameter)"); if (source != null) { if (source.Is_Aggregate ()) Source_Parameters = source; else { Source_Parameters = new Parameter (DEFAULT_SOURCE_PARAMETERS_NAME) .Classification (Parameter.GROUP); try {Source_Parameters.Add (source);} catch (PVL_Exception exception) {/* Known to be an Aggregate */} } // Set the current source in the PVL_to_DB Group. Update_Source_Name_Parameters (); } return this; } /** Sets the source parameter(s) to be mapped. @param source A PVL source String. This may be a URL, local pathname, or a system resource. If null, nothing is done. @throws PVL_Exception If there was a problem reading from or parsing the source stream. @throws Configuration_Exception If there was a problem updating the default parameters from the source name. @see Streams#Get_Stream(String) @see #Source_Parameters(Parameter) */ public PVL_to_DB Source_Parameters ( String source ) throws PVL_Exception, Configuration_Exception { if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println (">-< PLV_to_DB.Source_Parameters: " + source); return Source_Parameters (Assemble_PVL (source)); } /*------------------------------------------------------------------------------ Default Parameters */ /** Finds a PVL_to_DB Group in a Parameter list.

    @param parameters The Aggregate Parameter to search. @return The top level {@link #PVL_to_DB_GROUP} Aggregate, or null if not found. The first group found is returned. */ public static Parameter PVL_to_DB_Group ( Parameter parameters ) { if (parameters == null || ! parameters.Is_Aggregate () || parameters.List_Size () == 0) return null; Parameter parameter = null; while ((parameter = parameters.Find (Configuration.Absolute_Pathname (PVL_to_DB_GROUP), false, parameter)) != null && ! parameter.Is_Aggregate ()); if (parameter != null && ! parameter.Is_Aggregate ()) parameter = null; return parameter; } /** Removes all PVL_to_DB Groups in a Parameter list.

    @param parameters The Aggregate Parameter to search. @return The last top level {@link #PVL_to_DB_GROUP} Aggregate that was removed; null otherwise. */ public static Parameter Remove_PVL_to_DB_Group ( Parameter parameters ) { if (parameters == null || ! parameters.Is_Aggregate () || parameters.List_Size () == 0) return null; Parameter removed = null; Parameter parameter = null; while ((parameter = PVL_to_DB_Group (parameters)) != null) removed = parameters.Remove (parameter); return removed; } /** Adds new parameters to the default PVL_to_DB Group.

    The PVL_to_DB Group of parameters is used as defaults when resolving source parameter references in the map that are not resolved by the {@link #Source_Parameters(Parameter) Source Parameters}. Just before the {@link #PVL_Source() source mapping operation} begins the default parameters are appended to the source parameters which ensures that the default parameters will be present for source parameter resolution, but the default parameters will not override any source parameters having the same name.

    If the parameter is an Assignment it is added to the {@link #PVL_to_DB_GROUP PVL_to_DB Group} of default parameters. If the Parameter is an Aggregate and it contains a PVL_to_DB Group, the parameters in that group are added to the PVL_to_DB Group; otherwise all the Parameters of the Aggregate are added to the PVL_to_DB Group. The PVL_to_DB Group is Coalesced with its {@link Configuration#Duplicate_Parameter_Action(int) duplicate parameter action} set so that newly added duplicate parameter names will take precedence.

    If the Parameter is null the PVL_to_DB Group is reset with the default parameters, which are always included:

    {@link #HOSTNAME_PARAMETER}
    The name of the host system.
    {@link #CWD_PARAMETER}
    The pathname to the current working directory.
    {@link #SOURCE_NAME_PARAMETER}
    The current {@link #PVL_Source() PVL_Source} name.
    {@link #SOURCE_PATHNAME_PARAMETER}
    The absolute pathname of the current {@link #PVL_Source() PVL_Source}. This will be empty if the file does not exist.
    {@link #SOURCE_DIRECTORY_PARAMETER}
    The directory pathname of the SOURCE_FILENAME_PARAMETER. This will be empty if the file does not exist.
    {@link #SOURCE_FILENAME_PARAMETER}
    The filename portion of the SOURCE_PATHNAME_PARAMETER. This will be empty if the file does not exist.
    {@link #SOURCE_FILENAME_ROOT_PARAMETER}
    The SOURCE_FILENAME_PARAMETER value with the extension removed. This will be empty if the file does not exist.
    {@link #SOURCE_FILENAME_EXTENSION_PARAMETER}
    The extension portion of the SOURCE_FILENAME_PARAMETER. This will be empty if the filename has no extension following a period ('.') character, or if the file does not exist.
    {@link #LOOP_COUNTER_PARAMETER}
    The {@link #Loop() Loop} counter; initialized to 0.

    @param parameter The parameter(s) to be added to the PVL_to_DB default source parameters group. @return This PVL_to_DB object. @throws Configuration_Exception If there is a problem adding a new parameter. @see #Default_Parameter(String, Object) */ public PVL_to_DB Default_Parameters ( Parameter parameter ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (">>> PVL_to_DB.Default_Parameters"); if (parameter == null) { if (PVL_to_DB_Group == null) { // Should only happen at startup. if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Constructing empty " + PVL_to_DB_GROUP + " parameters group."); PVL_to_DB_Group = new Configuration ((Parameter)null); PVL_to_DB_Group.Name (PVL_to_DB_GROUP); PVL_to_DB_Group.Duplicate_Parameter_Action (Configuration.PREFER_LAST_PARAMETER); } else { // Remove all parameters. if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Resetting " + PVL_to_DB_GROUP + " parameters group."); PVL_to_DB_Group.Remove_All (); } try { // Add the default parameters. if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Adding required default parameters."); Default_Parameter (HOSTNAME_PARAMETER, Host.Hostname ()); Default_Parameter (CWD_PARAMETER, System.getProperty ("user.dir")); Update_Source_Name_Parameters (); Default_Parameter (LOOP_COUNTER_PARAMETER, new Integer (0)); } catch (Configuration_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to set the default parameters in the " + PVL_to_DB_GROUP + " group." + NL + exception.getMessage ())); } } else { if (PVL_to_DB_Group == null) // Provide the default parameters. Default_Parameters (null); Parameter group = parameter; try { if (parameter.Is_Aggregate ()) { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Adding optional default parameters."); if ((group = PVL_to_DB_Group (parameter)) == null) { // Add all parameters. if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Using all parameters -"); group = parameter; } else if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Only using " + PVL_to_DB_Group + " group -"); // Copy the Parameter list into the internal PVL_to_DB Group. if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (group.Description ()); group = new Parameter (group); PVL_to_DB_Group.Add (group.List ()); } else { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (" Adding optional default parameter -" + NL + parameter.Description ()); PVL_to_DB_Group.Add (parameter); } // Coalesce the parameters. PVL_to_DB_Group.Coalesce (); } catch (PVL_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to add default parameters from " + group.Path_Name () + NL + exception.getMessage ())); } } if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (PVL_to_DB_Group.Description () +"<<< PVL_to_DB.Default_Parameters"); return this; } /** Gets the default PVL_to_DB Group of parameters.

    @return The PVL_to_DB Aggregate Parameter. */ public Parameter Default_Parameters () {return PVL_to_DB_Group;} /** Adds a new parameter to the default PVL_to_DB Group.

    @param name The name of the parameter to be set. @param value An Object to use for the parameter's value. @return true if an existing parameter by the same name was replaced; false if the parameter is being set for the first time. @throws Configuration_Exception If there is a problem adding a new parameter. @see #Default_Parameters(Parameter) */ public boolean Default_Parameter ( String name, Object value ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (">-< PVL_to_DB.Default_Parameter: " + name + " = " + value); try {return PVL_to_DB_Group.Set (name, value);} catch (Configuration_Exception exception) { throw new Configuration_Exception (Error_Message ("Unable to set default parameter: " + name + " = " + value + NL + exception.getMessage ())); } } /** Updates the SOURCE_XXX parameters in the PVL_to_DB group.

    The {@link #SOURCE_NAME_PARAMETER} is set to the name of the {@link #Source_Parameters()} Aggregate. If there is none (it is null) then the name will be the empty string.

    The {@link #SOURCE_PATHNAME_PARAMETER}, {@link #SOURCE_DIRECTORY_PARAMETER}, {@link #SOURCE_FILENAME_PARAMETER}, {@link #SOURCE_FILENAME_ROOT_PARAMETER}, and {@link #SOURCE_FILENAME_EXTENSION_PARAMETER} Parameters are set to the corresponding components of the {@link #Source_Parameters()} Aggregate name taken to be a pathname (absolute or relative). If, however, there is no Aggregate or its name does not refer to an existing file, then these parameters will all be given the empty string as their value. The (@link LOOP_COUNTER_PARAMETER} is reset to zero. */ private void Update_Source_Name_Parameters () throws Configuration_Exception { String source_name = ""; if (Source_Parameters != null) source_name = Source_Parameters.Name (); Default_Parameter (SOURCE_NAME_PARAMETER, source_name); File file = new File (source_name); if (file.exists ()) { if (! file.isAbsolute ()) file = new File (file.getAbsolutePath ()); Default_Parameter (SOURCE_PATHNAME_PARAMETER, file.getAbsolutePath ()); Default_Parameter (SOURCE_DIRECTORY_PARAMETER, file.getParent ()); source_name = file.getName (); Default_Parameter (SOURCE_FILENAME_PARAMETER, source_name); int index = source_name.lastIndexOf ('.'); if (index < 0) index = source_name.length (); Default_Parameter (SOURCE_FILENAME_ROOT_PARAMETER, source_name.substring (0, index)); if (index < source_name.length ()) index++; Default_Parameter (SOURCE_FILENAME_EXTENSION_PARAMETER, source_name.substring (index)); } else { Default_Parameter (SOURCE_PATHNAME_PARAMETER, ""); Default_Parameter (SOURCE_DIRECTORY_PARAMETER, ""); Default_Parameter (SOURCE_FILENAME_PARAMETER, ""); Default_Parameter (SOURCE_FILENAME_ROOT_PARAMETER, ""); Default_Parameter (SOURCE_FILENAME_EXTENSION_PARAMETER, ""); } } /** Updates the {@link #LOOP_COUNTER_PARAMETER} in the Source_Parameters.

    @param count The new loop count value. */ private void Update_Loop_Counter_Parameter ( int count ) { if (Source_Parameters != null) { Parameter parameter = Source_Parameters.Find (Configuration.Absolute_Pathname (PVL_to_DB_GROUP, LOOP_COUNTER_PARAMETER)); if (parameter != null) { try {parameter.Value (count);} catch (PVL_Exception exception) {/* Shouldn't happen */} } } } /** Gets the value of the loop counter.

    This will be the number of times the last {@link #PVL_Source() PVL_Source} was successfully mapped to the Database. */ public int Loop_Count () { if (Source_Parameters != null) { Parameter parameter = Source_Parameters.Find (Configuration.Absolute_Pathname (PVL_to_DB_GROUP, LOOP_COUNTER_PARAMETER)); if (parameter != null) { try {return (int)parameter.Value ().long_Data ();} catch (PVL_Exception exception) {/* Shouldn't happen. */} } } return 0; } /*============================================================================== Map */ /** Assembles a map of PVL source parameter references assigned to Database field references.

    A PVL_to_DB map is used to assign Parameter (or constant) values to Database fields. It provides the mapping from resolved source reference values to resolved database field references where the values are deposited. Note: No map Parameter Value may be an Array; there must be only one Parameter Value for each Database field reference. However, this Value may contain multiple source references that are combined to produce a single field value.

    Syntactically, the map is a Aggregate of PVL Parameters in which the name of each Assignment Parameter is a Database field reference and the Value is a source reference:

    Database_field_reference = Source_reference

    Nested references -

    Any reference may contain {@link Reference_Resolver nested references}. All nested references are recursively resolved before the reference that contains them. A nested reference may be either a configuration parameter reference or a database field reference. All nested references are enclosed in curly braces ('{' and '}'), with configuration parameter references being distinguished from database field references by the former having a leading dollar sign ('$') before the opening curly brace. All nested references will either be resolved to a value or the {@link #Default_Value(String) Default_Value} will be used (if the default value is null an Unresolved_Reference exception will be thrown).

    N.B.: Nested parameter references are resolved against the configuration parameters, not the source parameters. Only nested references are enclosed in curly braces. The final database field and parameter source references used to accomplish the value assignment must not be enclosed.

    Database field reference -

    Catalog.Table.Field:Key

    The components preceding the colon form a fully qualified field name, and the component following the colon is a match (SQL WHERE) criteria used to identify a specific record from the database.

    Insert parameters

    Map parameters that have a database reference without a key will insert a new record in the field of the table specified by the reference.

    Update parameters

    Map parameters that have a database reference with a key are used to specify existing database fields to be updated with a new value. The key is the criteria that identifies the specific record of the table that is to be updated. The key is required to uniquely identify a single record.

    Update or Insert

    A database reference may be flagged with an {@link #UPDATE_OR_INSERT_MARKER} prefix character that will cause an update to be tried if the reference includes a key. If the update fails then an insert will be performed. If the reference does not include a key then only the insert is done. The special prefix marker is, of course, removed from the database reference before it is used.

    Source reference -

    A source reference is the Value of a map's Assignment Parameter. It is the expression that must be resolved to a value that is assigned to the database field. Ultimately each source reference must resolve to a single string value, which may be the text representation of a numeric value. However, a source reference is evaluated as a sequence of tokens that are individually resolved to string values that are concatenated to form the final source reference resolution. See the description of {@link #Resolve_Source_References(String) source references resolving} for the details of the source reference expression syntax.
    Parameter source reference -
    The basic source reference names a parameter in the source file which has an assignment value that is used to satisfy the reference. The syntax of a parameter source reference is the sames as the syntax for a nested parameter reference, but without the enclosing "${" and "}" characters:

    Pathname@Sequence[Index]...

    The Pathname is a simple, relative or absolute Parameter pathname used to {@link PIRL.PVL.Parameter#Find(String) Find} the parameter in the {@link #PVL_Source() PVL source}. How the pathname matching is done - whether case sensitive or using a regular expression pattern - is controlled by by the {@link #Match_Mode(int) match mode}. The Sequence, with its preceeding "at" ('@') delimiter , is optional; if present it indicates which of a multiple sequence parameters that match the Pathname to select (the first parameter is 0, which is the default). The Index, with its enclosing square bracket ('[' and ']') characters, is also optional; if present it indicates which element of an Array Value to select (the first element is 0, which is the default). Multiple Array Value element specifiers may be provided to select from multidimensional Array Values. Both the Sequence and Index specifiers may contain nested references. They may also be mathematical expressions that evaluate to any numeric value, which is truncated to an integer for selecting Array Value elements. The typical parameter source reference is simply a name of a source parameter.

    N.B.: Source parameter references are resolved against the source parameters, not the configuration parameters.

    Groups

    Map parameters that are contained within an Aggregate (Group) Parameter define the field values for a single record in the Database. The name of the Group is a Database {@link Database#Table_Reference(String, String) table reference} (in Catalog.Table notation) that specifies the table where the record will located; that is it is a database field reference without the Field portion. Each parameter within the Group specifies a field name and its source reference value, therefore its database reference can contain just the field name without the catalog and table name portions (if these are present, however, they must be the same as the table reference in the Group name); the table reference portion is implicitly the name of the Group.

    N.B.: As a special case, if the Group name contains nested references that can not be resolved against the configuration parameters an attempt will be made to resolve against both the configuration parameters and the current source parameters, in that order. This is a deprecated feature that could result in confusion of the correct parameter to resolve a nested reference. Retry can be disabled by setting Retry_Group_with_Source to false.

    The field names and values of a Group are all collected together to produce a single operation on the Database. Groups, therefore, are particularly useful for specifying all the fields of a single new record to be inserted into a table; the alternative is to insert a single field of a new record and then apply updates to this new record to provide additional field values. If the database reference of the Group name includes a key, then a single Database update operation will be done. N.B.: Depending on the particular database server, this could result in only some of the updates being completed if there is an error in one of the map references within the Group.

    Quotes and Escapes

    Because source references may be complex expressions and are likely to contain special PVL syntax characters (e.g. '{', '}', '=', quotes and spaces), the entire Value portion of a Map Parameter should be quoted (with either single or double quote characters) to prevent the misinterpretation of the special characters. This is a common source of syntax errors.

    When characters that would otherwise be interpreted as nested reference enclosure characters, or other special parse control characters (such as quotes and parentheses), need to be passed through as the value of the reference preceed these characters with a backslash ('\') which will escape the usual interpretation of the following character and remove the backslash. The backslash character itself may be escaped in this way.

    @param source The name of the PVL source (URL or pathname) containing the PVL that defines the map. @return A PVL Parameter Aggregate containing the mapping structure. @throws PVL_Exception If there is a problem parsing the PVL. */ public PVL_to_DB Map ( String source ) throws PVL_Exception { The_Map = Assemble_PVL (source); return this; } /** Sets the map parameters.

    @param map The map Parameter Aggregate. @return This PVL_to_DB object. @throws IllegalArgumentException If the map is not an Aggregate Parameter. */ public PVL_to_DB Map ( Parameter map ) throws IllegalArgumentException { if (map != null && ! map.Is_Aggregate ()) throw new IllegalArgumentException (Error_Message ( "The Map is not an Aggregate - " + map.Name ())); The_Map = map; return this; } /** Gets the map Parameters.

    @return The map Parameter Aggregate. @see #Map(Parameter) */ public Parameter Map () {return The_Map;} /*============================================================================== Mappers */ /** The current source of PVL parameters is mapped into the Database.

    The {@link #Loop_Count() loop counter} is first reset to zero.

    If there are no source parameters or map, then nothing is done.

    Any top level {@link #PVL_to_DB_GROUP} Aggregates are removed (as would occur if a source were reprocessed for any reason). Then the {@link #Default_Parameters(Parameter) default parameters} are appended to the {@link #Source_Parameters(Parameter) source parameters}.

    The map processing loop is entered. The loop counter is incremented at the successful completion of this loop, but the loop number (the first loop is number 1) is reported in verbose mode. In verbose mode all mapping operations - insert or update - and their values - both before and after reference resolution - will be reported. At the end of each map processing loop the number of updates effected on the database and records that were affected is listed, and after map processing completes the totals of these values will be listed. If map {@link #Loop(boolean) looping} has been enabled the map processing loop will be run until the loop counter reaches the {@link #Loop_Limit} or no source parameters can be resolved during a processing loop. The map will be processed only once if looping is disabled.

    Each map parameter is processed in the order it occurs in the map. A database reference is obtained from each parameter name and resolved of all nested references. From this a table reference and key is identified. Each database reference must contain a table reference, but the key is only present for update (not insert) operations. For Assignments the parameter value is resolved to a source reference (the details are provided in the description of the {@link #Map(String) map} syntax). For Aggregates all of the Assignment parameters it contains have their the database field references and resolved parameter references collected together before a database operation will be performed. If a source parameter reference can not be resolved the parameter is ignored. N.B.: Nested parameter references may be resolved by a {@link #Default_Value(String) default value} or throw an Unresolved_Reference exception if no default has been specified, but this only applies to nested references; after resolving nested references the result is still treated as a parameter reference but without a default (or exception). If at least one source parameter reference has been resolved, and {@link #No_Op(boolean) no-op} mode has not been enabled, then the Database is updated.

    @return The total number of database record updates/inserts. @throws ParseException If a reference could not be resolved due to invalid syntax. @throws Database_Exception If there is a problem accessing the database. @throws Invalid_Map_Syntax If the map parameters contain a invalid syntax; such as the lack of a valid table reference, an assignment parameter without a field name, an aggregate contained inside another aggregate, or an aggregate parameter entry with a table reference that does not match it's conataining aggregate. @throws Unresolved_Reference If a nested reference could not be resolved and there was no {@link #Default_Value(String) Default_Value}. @see Update_DB#Update_Database(String, String, Vector, Vector) */ public int PVL_Source () throws ParseException, Database_Exception, Invalid_Map_Syntax, Unresolved_Reference { if ((DEBUG & DEBUG_SOURCE) != 0) { System.out.println (">>> PLV_to_DB.PVL_Source"); Verbose = true; } if (Source_Parameters == null || The_Map == null) { if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println ("<<< PVL_to_DB.PVL_Source: 0 (no " + ((Source_Parameters == null) ? "source" : "map") + ")"); return 0; } // Remove any existing PVL_to_DB groups. Remove_PVL_to_DB_Group (Source_Parameters); // Append the default PVL_to_DB group parameters to the source parameters. try {Source_Parameters.Add (PVL_to_DB_Group);} catch (PVL_Exception exception) {/* Known to be an Aggregate */} if (Verbose || No_Op) System.out.println ("PVL source: " + Source_Parameters.Name ()); if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println (" PLV_to_DB.PVL_Source: PVL -" + NL + Source_Parameters.Description ()); int loop = 0, // Map processing loop counter. loop_limit = (Loop ? Loop_Limit : 1), records = 0, // Record updates per loop (from Update_Database). updates = -1, // Database updates per loop. total_records = 0, // Total number of record updates. total_updates = 0; // Total number of updates. Parameter map_parameter, parameter; String database_reference, table_reference, table_name, field, value, key, comment; boolean update_or_insert; Vector fields = new Vector (), values = new Vector (); // Map processing: for (loop = 0; updates != 0 && loop < loop_limit; loop++) { Update_Loop_Counter_Parameter (loop); if (loop_limit > 1 && (Verbose || No_Op)) System.out.println (NL +"*** Loop " + (loop + 1)); updates = records = 0; Iterator map_parameters = The_Map.iterator (); while (map_parameters.hasNext ()) { map_parameter = (Parameter)map_parameters.next (); // Clear the vectors of field names and values to be updated. values.clear (); fields.clear (); if (Verbose || No_Op) { if (map_parameter.Is_Aggregate ()) System.out.println ("=== Group " + map_parameter.Name ()); else { comment = map_parameter.Comments (); map_parameter.Comments (null); System.out.println ("--- " + map_parameter.Description ()); map_parameter.Comments (comment); } } // Database reference from parameter. database_reference = Database_Reference (map_parameter, map_parameter.Is_Aggregate () ? Source_Parameters : null); if (database_reference == null || database_reference.length () == 0 || database_reference.charAt (0) != UPDATE_OR_INSERT_MARKER) update_or_insert = false; else { database_reference = database_reference.substring (1); update_or_insert = true; } // Table reference from database reference if ((table_reference = Table_Reference (database_reference)) == null) throw new Invalid_Map_Syntax (Error_Message ("No table reference for map parameter database reference."), database_reference, map_parameter); // Key from database reference. key = The_Resolver.Key (database_reference); if (map_parameter.Is_Aggregate ()) { if (Verbose || No_Op) { if (key == null) System.out.println ("=== Insert " + table_reference); else System.out.println ("=== Update " + (update_or_insert ? "or Insert " : "") + table_reference + The_Resolver.KEY_MARKER + key); } // Collect all of the Aggregate's field names and values. Iterator parameters = map_parameter.iterator (); while (parameters.hasNext ()) { parameter = (Parameter)parameters.next (); if (Verbose || No_Op) { comment = parameter.Comments (); parameter.Comments (null); System.out.println ("==> " + parameter.Description ()); parameter.Comments (comment); } // Parameter reference. try {value = Resolve_Source_Reference (parameter);} catch (Database_Exception exception) { throw new Database_Exception (exception.getMessage () + NL +"Parameter group: " + map_parameter.Name ()); } catch (ParseException exception) { throw new ParseException (exception.getMessage () + NL +"Parameter group: " + map_parameter.Name (), exception.getErrorOffset ()); } catch (Unresolved_Reference exception) { throw new Unresolved_Reference (exception.getMessage () + NL +"Parameter group: " + map_parameter.Name ()); } if (value == null) { if (Verbose || No_Op) System.out.println ("!!! No resolution."); // Didn't resolve to a value. continue; } // Database reference. database_reference = Database_Reference (parameter); // Table reference from database reference table_name = Table_Reference (database_reference); if (table_name != null && ! The_Database.Matches (table_name, table_reference)) throw new Invalid_Map_Syntax (Error_Message ("Map aggregate table reference \"" + table_reference + '"' + NL +"does not match the map parameter database reference."), table_name, parameter); // Field name. if ((field = The_Resolver.Field_Name (database_reference)) == null) throw new Invalid_Map_Syntax (Error_Message ("No field name in map parameter database reference."), database_reference, parameter); fields.add (field); values.add (value); if (Verbose || No_Op) System.out.println (" " + field + " = " + value); } } else // Top level Assignment. { // Parameter reference. value = Resolve_Source_Reference (map_parameter); if (value == null) { if (Verbose || No_Op) System.out.println ("!!! No resolution."); // Didn't resolve to a value. continue; } // Field name. if ((field = The_Resolver.Field_Name (database_reference)) == null) throw new Invalid_Map_Syntax (Error_Message ("No field name in map parameter database reference."), database_reference, map_parameter); fields.add (field); values.add (value); if (Verbose || No_Op) System.out.println ("--> " + ((key == null) ? "Insert" : "Update") + ' ' + table_reference + The_Resolver.COMPONENTS_DELIMITER + field + " = " + value); } if (! fields.isEmpty ()) { if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println (" PLV_to_DB.PVL_Source: Update_Database -" + NL +" table_reference: " + table_reference + NL +" key: " + key + NL +" fields: " + fields + NL +" values: " + values); if (! No_Op) { Exception exception = null; try {records += Update_Database (table_reference, key, fields, values);} catch (Database_Exception except) { except = new Database_Exception (Error_Message (except.getMessage () + NL +"While mapping " + (map_parameter.Is_Aggregate () ? ("group " + map_parameter.Name ()) : (map_parameter.Description ())))); if (key == null || ! update_or_insert) throw except; exception = except; } catch (IllegalArgumentException except) { if (key == null || ! update_or_insert) throw except; exception = except; } if (exception != null) { // Try to insert instead of update. try {records += Update_Database (table_reference, null, fields, values);} catch (Database_Exception except) { throw new Database_Exception (Error_Message (except.getMessage () + NL +"While mapping " + (map_parameter.Is_Aggregate () ? ("group " + map_parameter.Name ()) : (map_parameter.Description ())) + NL + NL +"During insert after record update failed -" + NL + exception.getMessage ())); } } } ++updates; } } if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println (" ----------------------------------------------------------------" + NL); total_updates += updates; total_records += records; if (Verbose || No_Op) System.out.println (NL +"Updates effected: " + updates + NL +"Records affected: " + records); } Update_Loop_Counter_Parameter (loop); if (loop > 1 && (Verbose || No_Op)) System.out.println (NL +"Total updates effected: " + updates + NL +"Total records affected: " + records); if ((DEBUG & DEBUG_SOURCE) != 0) System.out.println ("<<< PVL_to_DB.PVL_Source: " + total_records); return total_records; } /** Names a source of PVL Parameters which is mapped into the Database.

    @param source The name of the PVL source (a local pathname or URL). If null, the previous PVL source is used. If there is no previous PVL source, nothing is done. @return The total number of database record updates/inserts. @throws PVL_Exception If there is a problem parsing the PVL from the input file. @throws Configuration_Exception If there was a problem updating the default parameters from the source name. @throws ParseException If a reference could not be resolved due to invalid syntax. @throws Database_Exception If there is a problem accessing the database. @throws Invalid_Map_Syntax If the map parameters contain a invalid syntax; such as the lack of a valid table reference, an assignment parameter without a field name, an aggregate contained inside another aggregate, or an aggregate parameter entry with a table reference that does not match it's conataining aggregate. @throws Unresolved_Reference If a nested reference could not be resolved and there was no {@link #Default_Value(String) Default_Value}. @see #Source_Parameters(String) @see #PVL_Source() */ public int PVL_Source ( String source ) throws PVL_Exception, Configuration_Exception, ParseException, Database_Exception, Invalid_Map_Syntax, Unresolved_Reference { // Set the source of parameters (no change if null). Source_Parameters (source); // Map the source parameters to the database. return PVL_Source (); } /** Supplies a source of PVL Parameters which is mapped into the Database.

    @return The total number of database record updates/inserts. @throws Configuration_Exception If there was a problem updating the default parameters from the source name. @throws ParseException If a reference could not be resolved due to invalid syntax. @throws Database_Exception If there is a problem accessing the database. @throws Invalid_Map_Syntax If the map parameters contain a invalid syntax; such as the lack of a valid table reference, an assignment parameter without a field name, an aggregate contained inside another aggregate, or an aggregate parameter entry with a table reference that does not match it's conataining aggregate. @throws Unresolved_Reference If a nested reference could not be resolved and there was no {@link #Default_Value(String) Default_Value}. @see #Source_Parameters(Parameter) @see #PVL_Source() */ public int PVL_Source ( Parameter source ) throws Configuration_Exception, ParseException, Database_Exception, Invalid_Map_Syntax, Unresolved_Reference { // Set the source of parameters (no change if null). Source_Parameters (source); // Map the source parameters to the database. return PVL_Source (); } /*------------------------------------------------------------------------------ Map reference resolvers */ /** Gets a database reference from a map parameter.

    The name of the map parameter contains the database reference. All nested references in the name are resolved.

    If the initial attempt to resolve the map parameter name fails due to an Unresolved_Reference exception and retry parameters are provided the retry parameters will be temporarily bound to the reference resolver, after saving the previously bound parameters, and another attempt will be made to resolve the map parameter name. If this fails for any reason an Unresolved_Reference will be thrown. The original exception message will be used supplemented with the new exception message if the latter is different from the former. The parameters previously bound to the reference resolver will always be restored. N.B.: This is a deprecated workaround to provide backwards compatibility with a previous logic flaw and is expected to be removed in a future release.

    @param map_parameter The map parameter which has a database reference as its name. @param retry_parameters Parameters to use for resolver retry. If null no retry is attempted. @return The resolved database reference String. @throws ParseException if a nested reference could not be resolved due to invalid syntax. @throws Database_Exception if there was a problem accessing the database to resolve a nested database reference. @throws Unresolved_Reference if a nested reference could not be resolved and there was no {@link #Default_Value(String) Default_Value}. */ private String Database_Reference ( Parameter map_parameter, Parameter retry_parameters ) throws ParseException, Database_Exception, Unresolved_Reference { if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (">>> PVL_to_DB.Database_Reference: " + map_parameter.Name ()); String database_reference = null; try {database_reference = The_Resolver.Resolve (map_parameter.Name ());} catch (ParseException exception) { throw new ParseException (Error_Message ("For the database reference of map entry" + NL + (map_parameter.Is_Aggregate () ? (map_parameter.Name () + NL) : map_parameter.Description ()) + exception.getMessage ()), exception.getErrorOffset ()); } catch (Database_Exception exception) { throw new Database_Exception (Error_Message ("For the database reference of map entry" + NL + (map_parameter.Is_Aggregate () ? (map_parameter.Name () + NL) : map_parameter.Description ()) + exception.getMessage ())); } catch (Unresolved_Reference exception) { String message = exception.getMessage (); Unresolved_Reference unresolved_reference = new Unresolved_Reference (Error_Message ("For the database reference of map entry" + NL + (map_parameter.Is_Aggregate () ? (map_parameter.Name () + NL) : map_parameter.Description ()) + message), map_parameter.Name ()); if (Retry_Group_with_Source && retry_parameters != null) { if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" PVL_to_DB.Database_Reference: Retry with source parameters" + NL +" after exception -" + NL + unresolved_reference.getMessage ()); /* Retry Bind the Reference_Resolver to the retry_parameters supplemented by the currently bound parameters. */ Parameter config_parameters = The_Resolver.Parameters (), parameters = null, source_params = null; try { parameters = new Parameter (config_parameters); source_params = new Parameter (retry_parameters); if (source_params.Is_Aggregate ()) parameters.Add (source_params.List ()); else parameters.Add (source_params); } catch (PVL_Exception e) {} The_Resolver.Parameters (parameters); // Ensure no default value for source parameter reference resolution. String default_value = The_Resolver.Default_Value (); The_Resolver.Default_Value (null); try {database_reference = The_Resolver.Resolve (map_parameter.Name ());} catch (Exception except) { if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Retry failed -" + NL + except.getMessage ()); if (! except.getMessage ().equals (message)) unresolved_reference = new Unresolved_Reference (Error_Message ("For the database reference of map entry" + NL + (map_parameter.Is_Aggregate () ? (map_parameter.Name () + NL) : map_parameter.Description ()) + message + NL + "After retry with source parameters -" + NL + except.getMessage ()), map_parameter.Name ()); throw unresolved_reference; } finally { // Restore The_Resolver nested reference context. The_Resolver.Default_Value (default_value); The_Resolver.Parameters (config_parameters); } if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Retry succeeded"); } else throw unresolved_reference; } if ((DEBUG & DEBUG_FIELD_REFERENCE) != 0) System.out.println ("<<< PVL_to_DB.Database_Reference: " + database_reference); return database_reference; } /** Gets a database reference from a map parameter.

    @param map_parameter The map parameter which has a database reference as its name. @return The resolved database reference String. @throws ParseException if a nested reference could not be resolved due to invalid syntax. @throws Database_Exception if there was a problem accessing the database to resolve a nested database reference. @throws Unresolved_Reference if a nested reference could not be resolved and there was no {@link #Default_Value(String) Default_Value}. @see #Database_Reference(Parameter, Parameter) */ private String Database_Reference ( Parameter map_parameter ) throws ParseException, Database_Exception, Unresolved_Reference {return Database_Reference (map_parameter, null);} /** Gets a table reference from a database reference.

    @param database_reference The database reference String from which to extract the table reference. @return The catalog.table reference, or null if no table reference is present (maybe just a field name). @see Reference_Resolver#Table_Name(String) @see Database#Table_Reference(String) @see Reference_Resolver#Catalog_Name(String) */ private String Table_Reference ( String database_reference ) throws Database_Exception { if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (">>> PVL_to_DB.Table_Reference: " + database_reference); /* Add the table name to the catalog name from the database reference. N.B.: If there is no table name, there will be no catalog name. */ String table_reference = The_Resolver.Table_Name (database_reference); if (table_reference != null) { try {table_reference = The_Database.Table_Reference (The_Resolver.Catalog_Name (database_reference), table_reference);} catch (Database_Exception exception) { /* Shouldn't happen. */ table_reference = null; } } if ((DEBUG & (DEBUG_FIELD_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println ("<<< PVL_to_DB.Table_Reference: " + table_reference); return table_reference; } /** Resolve all references in a Parameter Value.

    The Parameter must be an Assignment with a Value that is not an Array. The Value as a String is submitted to {@link #Resolve_Source_Reference(String)}, with handlers for all exception conditions.

    @param parameter The map Parameter to have its Value resolved. @return The resolved String, or null if there was no resolution. @throws Invalid_Map_Syntax If the parameter is not an Assignment, its Value is an Array, or a unresolved source reference resulted in a forced exception. @throws Database_Exception From {@link Resolve_Source_References(String)}. @throws ParseException From {@link Resolve_Source_References(String)}. @throws Unresolved_Reference From {@link Resolve_Source_References(String)}. */ private String Resolve_Source_Reference ( Parameter parameter ) throws Invalid_Map_Syntax, ParseException, Database_Exception, Unresolved_Reference { if (! parameter.Is_Assignment ()) throw new Invalid_Map_Syntax (Error_Message ("Invalid " + parameter.Classification_Name () + " map parameter."), null, parameter); try { if (parameter.Value ().Is_Array ()) throw new Invalid_Map_Syntax (Error_Message ("Invalid " + parameter.Value ().Type_Name () + " of values for a map parameter."), null, parameter); } catch (PVL_Exception exception) {/* Checked for Aggregate above. */} try {return Resolve_Source_References (parameter.Value ().String_Data ());} catch (PVL_Exception exception) {/* Checked for Array above. */} catch (Database_Exception exception) { throw new Database_Exception (exception.getMessage () + NL +"Map parameter: " + parameter.Description ()); } catch (ParseException exception) { throw new ParseException (exception.getMessage () + NL +"Map parameter: " + parameter.Description (), exception.getErrorOffset ()); } catch (Unresolved_Reference exception) { throw new Unresolved_Reference (exception.getMessage () + NL +"Map parameter: " + parameter.Description ()); } catch (Invalid_Map_Syntax exception) { exception.Parameter (parameter); throw exception; } return null; } /** Resolves a Source reference to its value.

    A PVL source reference, as occurs in {@link #Map(String) Map} Parameter Values, is resolved to a value. The steps taken to achieve a resolved value are:

    The source reference is first resolved of all nested references. If there are no syntax errors or an exception due to an unresolved reference the result is evaluated for individual reference tokens.

    The source reference expression is searched for individual reference tokens. Each identified individual reference is resolved to a string value. The resolved values are concatenated in the left-to-right order they occur in the source reference to produce its resolved value. N.B.: Unquoted whitespace characters (any of " \t\r\n") are compressed out of the expression during the process of resolution value accumulation.

    Individual references are identified and resolved as follows, in the order in which they are listed:

    A logically empty string.
    The source reference is trimmed of leading and trailing whitespace characters. If the result is an empty string the resolved value is an empty string.
    A mathematical expression.
    An attempt is made to evaluate the source reference as a mathematical expression. If this succeeds the result, as a string representation, is the resolved value.

    The expression {@link edu.hws.jcm.data.Parser#Parser() parser} accepts both simple and complex syntax with numeric constants plus the symbolic constants pi and e, the usual mathematical operators (unary and binary + and -, * and /) plus exponentiation (either ^ or **) and a full complement of standard math functions (sin, cos, tan, cot, sec, csc, arcsin, arccos, arctan, exp, ln, log2, log10, sqrt, cubert, abs, round, floor, ceiling and trunc). Also, logical operators (&, |, ~, =, <, >, <>, <= and >=; the words "and", "or", and "not" can be used in place of &, | and ~) may be used in a conditional expression (with ? and :) as long as the result of the expression evaluation is a single numeric value. The evaluation will use double precision arithmetic and the result will be provided in its {@link Double#toString(double) string representation} form.

    N.B.: If the results of resolving a mathematical expression are equivalent to an integer, despite being returned as a double type, then the resolved value will be represented as an integer without a ".0" suffix.

    N.B.: Mathematical expression evaluation of an individual reference can be suppressed by preceding the reference with the {@link #NO_MATH_MARKER} character. In this case the marker is stripped off an no attempt is made to evaluate the reference or its resolved value as a mathematical expression.

    A quoted string.
    All characters within unescaped single or double quotes are taken verbatim. The quotation marks are stripped off and the resulting string is the resolved value. N.B.: Quoted strings do not need to be surrounded by whitespace; they may be embedded anywhere in a source reference. Conversely, to include whitespace characters in the resolved value of a source reference they must be quoted.

    A quotation mark without a closing unescaped matching quotation mark in the same source reference results in an {@link Invalid_Map_Syntax} exception being thrown that identifies the unclosed quoted string as the unresolved reference.

    A parenthesized expression.
    The characters within unescaped parentheses are recursively processed as a source reference to produce a resolved value. The enclosing parentheses are stripped off. Nested parentheses are included and, unless escaped, will be recognized during the recursive procssing of the new source reference. N.B.: Parenthetical expressions do not need to be surrounded by whitespace; they may be embedded anywhere in a source reference.

    An opening parenthesis without a closing unescaped matching closing parenthesis, ignoring nested parentheses, in the same source reference results in an {@link Invalid_Map_Syntax} exception being thrown that identifies the unclosed parenthetical string as the unresolved reference.

    An alternative marker.
    The occurance of an {@link #ALTERNATIVE_MARKER} character causes all further accumulation of resolved references in the current source reference to stop if, and only if, the immediately preceeding source reference (if any) was successfully resolved. That is, if the immediately preceeding reference fails to resolve then the occurance of an alternative marker causes the failed reference to be ignored.

    N.B.: An alternative marker may be followed by any source reference other than an alternative marker, including an empty string. If another alternative marker follows in the current source reference then an {@link Invalid_Map_Syntax} exception is thrown that identifies the current source reference.

    A source parameter reference.
    A contiguous sequence of non-whitespace characters that are not quoted or parenthesized and is not an alternative marker is evaluated as a source parameter reference. A source parameter reference is resolved against the current {@link #Source_Parameters(Parameter) source parameters}, with the {@link #Default_Parameters(Parameter) default parameters} appended, to produce a resolved value.

    If the source parameter reference is unresolved and the next reference in the current source reference is an alternative marker the unresolved reference is ignored and parsing continues with the next token. Note that if an alternative marker occurs at the end of a source reference it will never fail to resolve to at least the empty string value. However, if an alternative marker does not follow a source parameter reference that fails to resolve to a value then further parsing of the source reference stops and the enclosing source reference, if any, is signaled that an unresolved source reference occured; this source reference may be followed by an alternative marker. This enables any parenthesized expression to be provided with an alternative in case the entire parenthesized expression fails to resolve.

    After each individual source reference has been successfully resolved another attempt is made to evaluate the results as a mathematical expression, unless this has been suppressed by the use of the {@link #NO_MATH_MARKER} character in front of the reference. If this succeeds, the results are used as the resolved value; otherwise the original resolved value is used. N.B.: To include parameter source references in post-resolution mathematical expressions all parts of the expression that are not to be otherwise treated as resolvable references must be quoted. This is an admittedly awkward requirement, but otherwise all parameter source references would need special syntax identification which would be even more awkward.

    @param source_reference The reference String to be resolved to a Parameter source value. @return The resolved String value. @throws ParseException If the evaluation of a mathematical expression in a nested reference encounters a syntax error. @throws Database_Exception If a nested database reference can not be resolved. @throws Unresolved_Reference If a nested reference can not be resolved and there is no {@link #Default_Value(String) default reference value}. @throws Invalid_Map_Syntax If a parameter source reference can not be resolved, or a source parameter reference alternative is the quoted string constant {@link Conductor#UNRESOLVED_REFERENCE_THROWS "THROW"} (case insensitive). */ public String Resolve_Source_References ( String source_reference ) throws ParseException, Database_Exception, Unresolved_Reference, Invalid_Map_Syntax { if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (">>> PVL_to_DB.Resolve_Source_References:" + NL +" source reference: " + source_reference); // Resolve any embedded references in the source reference. String reference = null; try {reference = The_Resolver.Resolve (source_reference);} catch (ParseException exception) { throw new ParseException (Error_Message (exception.getMessage () + NL +"In source parameter reference \"" + source_reference + '"' + NL +"at location " + exception.getErrorOffset ()), exception.getErrorOffset ()); } catch (Database_Exception exception) { throw new Database_Exception (Error_Message (exception.getMessage () + NL +"In source parameter reference \"" + source_reference + '"')); } catch (Unresolved_Reference exception) { throw new Unresolved_Reference (Error_Message (exception.getMessage () + NL +"In source parameter reference \"" + source_reference + '"')); } if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" PVL_to_DB.Resolve_Source_References:" +" Source pattern resolved to - " + reference); // Bind the Reference_Resolver to the Source_Parameters. The_Resolver.Parameters (Source_Parameters); // Ensure no default value for source parameter reference resolution. String default_value = The_Resolver.Default_Value (); The_Resolver.Default_Value (null); String value = null; try {value = Resolve_Source_Reference (reference);} catch (Invalid_Map_Syntax exception) { if (! reference.equals (exception.Reference ())) throw new Invalid_Map_Syntax ((exception.getMessage () + NL +"in source parameter reference \"" + source_reference + "\""), exception.Reference ()); throw exception; } catch (Unresolved_Reference exception) { if (No_Op) System.out.println (" Unresolved reference: " + exception.Reference ()); else if (Unresolved_Source_Reference_Throws) { if (! reference.equals (exception.Reference ())) throw new Unresolved_Reference ((exception.getMessage () + NL +"in source parameter reference \"" + source_reference + "\""), exception.Reference ()); throw exception; } } catch (ParseException exception) { throw new ParseException ((exception.getMessage () + NL +"in source parameer reference \"" + source_reference + "\""), exception.getErrorOffset ()); } finally { // Restore The_Resolver nested reference context. The_Resolver.Default_Value (default_value); The_Resolver.Parameters (The_Database.Configuration ()); } if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println ("<<< PVL_to_DB.Resolve_Source_References: " + value); return value; } /** Identify and resolve each individual reference in a source reference.

    The source reference is searched for individual references. Each identified individual reference is resolved to a String value. The resolved values are appended contiguously to produce the resolved value of the source reference.

    @param source_reference A source reference String to be resolved. @return The resolved value String. @throws Unresolved_Reference If the current source reference could not be resolved. @throws Invalid_Map_Syntax If an unclosed quoted or parenthetical string occurs, or an alternative marker is followed by another alternative marker. @throws ParseException If there was a mathematical expression parsing problem during reference resolving. @throws Unresolved_Reference If the reference could not be resolved. @see #Resolve_Source_References(String) */ private String Resolve_Source_Reference ( String source_reference ) throws ParseException, Unresolved_Reference, Invalid_Map_Syntax { if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (">>> PVL_to_DB.Resolve_Source_Reference: " + source_reference); String reference = source_reference.trim (); if (reference.length () == 0) { System.out.println ("<<< PVL_to_DB.Resolve_Source_Reference: Empty"); return reference; } boolean evaluate_for_math_expression = true; if (reference.charAt (0) == NO_MATH_MARKER) { evaluate_for_math_expression = false; reference = reference.substring (1); if (reference.length () == 0) return reference; } if (evaluate_for_math_expression) { // Try to evaluate it as a mathematical expression. try { reference = number_representation (The_Resolver.Evaluate_to_double (reference)); if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Math expression evaluated" + NL +"<<< PVL_to_DB.Resolve_Source_Reference: " + reference); return reference; } catch (ParseException parse_exception) {} } String resolved = "", value = ""; Unresolved_Reference unresolved_reference = null; Words words = new Words (reference) .Quoted_Words (true) .Parenthesized_Words (true); Scan_for_References: while ((reference = words.Next_Word ()).length () != 0) { if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" > Reference - " + reference); switch (reference.charAt (0)) { case '"': case '\'': // Quoted string. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Quoted string - " + reference); unresolved_reference = null; // Got an alternative. if (reference.length () == 1 || reference.charAt (reference.length () - 1) != reference.charAt (0)) throw new Invalid_Map_Syntax (Error_Message ("Quoted string is not closed."), reference); value = reference.substring (1, reference.length () - 1); break; case '(': // Parenthesized expression. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Parenthesized expression - " + reference); unresolved_reference = null; // Got an alternative. if (reference.length () == 1 || reference.charAt (reference.length () - 1) != ')') throw new Invalid_Map_Syntax (Error_Message ("Parenthesized expression is not closed."), reference); try {value = Resolve_Source_Reference (reference.substring (1, reference.length () - 1));} catch (Unresolved_Reference exception) {unresolved_reference = exception;} break; case ALTERNATIVE_MARKER: if (unresolved_reference != null) // Already looking for an alternative. throw new Invalid_Map_Syntax (Error_Message ("Sequential alternative source reference markers (" + ALTERNATIVE_MARKER + ')' + NL +"after unresolved reference -" + NL + unresolved_reference.getMessage ()), source_reference); // Not looking for an alternative. Done. break Scan_for_References; default: // Parameter source reference. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Source reference - " + reference); unresolved_reference = null; // Got an alternative. try { value = The_Resolver.Resolve_Parameter_Reference (reference); if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Resolved reference - " + value); break; } catch (ParseException exception) { throw new ParseException (Error_Message (exception.getMessage () + NL +"At location " + exception.getErrorOffset () + NL +"in source reference \"" + reference + "'" + (source_reference.equals (reference) ? "" : (NL + "of \"" + source_reference + '"'))), exception.getErrorOffset ()); } catch (Unresolved_Reference exception) {unresolved_reference = exception;} break; } if (unresolved_reference != null) { if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Reference did not resolve"); // Check for an alternative reference marker. reference = words.Next_Word (); if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Next word - " + reference); if (reference.length () == 0 || reference.charAt (0) != ALTERNATIVE_MARKER || reference.length () > 2 || (reference.length () == 2 && reference.charAt (1) != ALTERNATIVE_MARKER)) { // No alternative. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println ("<<< PVL_to_DB.Resolve_Source_Reference: Unresolved"); throw unresolved_reference; } // Take the alternative. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Alternative ..."); continue; } // Accumulate the resolved reference. if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Resolved reference - " + value); resolved += value; } if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Resolution: " + resolved); if (evaluate_for_math_expression) { // Try to evaluate it as a mathematical expression (just in case :-). try { resolved = number_representation (The_Resolver.Evaluate_to_double (resolved)); if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println (" Math expression evaluated"); } catch (ParseException e) {} } if ((DEBUG & (DEBUG_PARAMETER_REFERENCE | DEBUG_SOURCE)) != 0) System.out.println ("<<< PVL_to_DB.Resolve_Source_Reference: " + resolved); return resolved; } /** Get the number representation of a double value. If the value is equivalent to an integer it's number representation is as the integer; i.e. no ".0" suffix is included. @param value The double to be represented. @return The String representation of the value. */ private static String number_representation ( double value ) { if (value == (int)value) return String.valueOf ((int)value); return String.valueOf (value); } /*============================================================================== Helpers */ /** Assembles an Aggregate by parsing a PVL source.

    @param source A PVL source String. This may be a URL, local pathname, or a system resource. @return An Aggregate Parameter. The Aggregate will be given the source Name. If the source is null, null will be returned. @throws PVL_Exception If there was a problem reading from or parsing the source stream. @see Streams#Get_Stream(String) */ private static Parameter Assemble_PVL ( String source ) throws PVL_Exception { if (source == null) return null; if ((DEBUG & DEBUG_MAP) != 0) System.out.println (">>> PVL_to_DB.Assemble_PVL: " + source); InputStream source_stream = Streams.Get_Stream (source); if (source_stream == null) throw new PVL_Exception (Error_Message ( "Unable to access source " + source + NL + " to assemble the parameters map.")); Parameter aggregate; try { aggregate = new Parameter (new Parser (source_stream) .Strict (false) .Crosshatch_Comments (true)); } catch (PVL_Exception exception) { throw new PVL_Exception (Error_Message ( "Unable to read the PVL to database map from the \"" + source + "\" source." + NL + exception.getMessage ())); } aggregate.Name (source); if ((DEBUG & DEBUG_MAP) != 0) System.out.println ("<<< PVL_to_DB.Assemble_PVL:" + NL + aggregate.Description ()); return aggregate; } /** Produces an error message with the application's ID.

    The message string will be prepended with the class {@link #ID ID} and a new-line, if the ID is not already present in the message. If the message is null, it will be replaced with the ID.

    @param message The message String. @return The modified message String. */ private static String Error_Message ( String message ) { if (message == null) message = ID; else if (message.indexOf (ID) < 0) message = ID + NL + message; return message; } /** Produces a Database_Exception containing a message.

    The message will be prepended with a description of the Database from the Configuration and then modified by the {@link #Error_Message(String) Error_Message} method before it is used to construct a new Database_Exception.

    @param message The message String. @return A Database_Exception. */ private Database_Exception Database_Error ( String message ) { if (The_Database == null) message = "No Database specified." + NL + message; else { Configuration configuration = The_Database.Configuration (); message = "Problem accessing " + configuration.Get_One (Database.TYPE) + " database on host " + configuration.Get_One (Configuration.HOST) + ":" + NL + message; } return new Database_Exception (Error_Message (message)); } /*============================================================================== Application main */ /** Runs a PVL_to_DB application.

    N.B.: If a Database connection is establshed it is always Disconnected before the application exits for any reason.

    Exit status values:

    {@link #EXIT_SUCCESS}
    Success
    {@link #EXIT_INVALID_COMMAND_LINE_SYNTAX}
    Invalid command line syntax
    {@link #EXIT_CONFIGURATION_PROBLEM}
    Configuration problem
    {@link #EXIT_DATABASE_ERROR}
    Database access error
    {@link #EXIT_PVL_ERROR}
    PVL processing problem
    {@link #EXIT_INVALILD_SYNTAX}
    Invalid syntax was found in the map
    {@link #EXIT_UNRESOLVED_REFERENCE}
    An unresolved reference was encountered

    @param arguments The {@link #Usage command line} arguments. */ public static void main ( String [] arguments ) { if (arguments.length == 0) Usage (true); PVL_to_DB mapper = null; String option, configuration_source = null, database_server = null, map_source = null, name; Vector PVL_sources = new Vector (); boolean verbose = false, noop = false, unresolved_source_reference_throws = false; if (DEBUG != 0) verbose = true; int looping = 0, matching = Reference_Resolver.CASE_SENSITIVE; Configuration user_parameters = null; try {user_parameters = new Configuration ((Parameter)null);} catch (Configuration_Exception exception) {} for (int count = 0; count < arguments.length; count++) { if (arguments[count].length () == 0) continue; if (arguments[count].charAt (0) == '-' && arguments[count].length() > 1) { option = arguments[count]; if (option.equalsIgnoreCase ("-SERVER")) option = "-Database"; switch (option.charAt (1)) { case 'C': // -Configuration case 'c': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { // Use the default configuration file. name = DEFAULT_CONFIGURATION_FILENAME; --count; } else name = arguments[count]; if (configuration_source == null) configuration_source = name; else { System.out.println ("Multiple configuration sources:" + NL +configuration_source + NL +"and" + NL +name); Usage (verbose); } break; case 'D': // -Database case 'd': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.out.println ("Missing database server name "); Usage (verbose); } name = arguments[count]; if (database_server == null) database_server = name; else { System.out.println ("Multiple database servers: " + database_server + " and " + name); Usage (verbose); } break; case 'H': // -Help case 'h': Usage (true); case 'L': // -Loop case 'l': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') looping = 0; else { try {looping = Integer.parseInt (arguments[count]);} catch (NumberFormatException exception) { System.out.println ("Loop count expected; but \"" + arguments[count] + "\" was found."); Usage (verbose); } } break; case 'M': // -Map case 'm': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.out.println ("Missing map source."); Usage (verbose); } name = arguments[count]; if (map_source == null) map_source = name; else { System.out.println ("Multiple map sources:" + NL + map_source + NL +"and" + NL + name); Usage (verbose); } break; case 'N': // -Noop case 'n': noop = true; break; case 'P': // -Pattern case 'p': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.out.println ("Missing pattern match selection."); Usage (verbose); } switch (arguments[count].charAt (0)) { case 'C': // Case sensitive. case 'c': matching = Reference_Resolver.CASE_SENSITIVE; break; case 'I': // Ignore case. case 'i': matching = Reference_Resolver.CASE_INSENSITIVE; break; case 'R': // Regex. case 'r': matching = Reference_Resolver.PATTERN_MATCH; break; default: System.out.println ("Unrecognized pattern match selection: " + arguments[count]); Usage (verbose); } break; case 'Q': // -Quiet case 'q': if (DEBUG == 0) verbose = false; break; case 'S': // -Set case 's': if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.out.println ("Missing parameter definition to set."); Usage (verbose); } try {user_parameters.Set (new Parameter (new Parser (arguments[count])));} catch (Exception exception) { System.out.println ("Unable to set the parameter definition: \"" + arguments[count] + '"' + NL + exception.getMessage ()); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } break; case 'u': case 'U': unresolved_source_reference_throws = true; break; case 'V': // -Verbose case 'v': verbose = true; break; default: System.out.println ("Unrecognized command line argument: " + arguments[count]); Usage (true); } } else PVL_sources.add (arguments[count]); } // Check requirements. if (map_source == null) { System.out.println ("No map source."); Usage (verbose); } if (PVL_sources.size () == 0) { System.out.println ("No PVL source file(s)."); Usage (verbose); } // Construct the Configuration. Configuration configuration = null; if (configuration_source == null) configuration_source = DEFAULT_CONFIGURATION_FILENAME; try {configuration = new Configuration (configuration_source);} catch (Configuration_Exception exception) { System.out.println (ID + NL + "Unable to construct a Configuration from the" + NL + configuration_source + " source." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } // Set user parameters from the command line. try {configuration.Set (user_parameters);} catch (Configuration_Exception exception) { System.out.println (ID + NL + "Unable to apply the parameter(s) set on the command line." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } // Establish the Database connection. Database database = null; try { database = new Database (configuration); database.Connect (database_server); } catch (Database_Exception exception) { System.out.println (ID + NL + "Unable to connect to the database." + NL + exception.getMessage ()); System.exit (EXIT_DATABASE_ERROR); } catch (Configuration_Exception exception) { System.out.println (ID + NL + "Unable to connect to the database." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } // Construct the PVL_to_DB object. try {mapper = new PVL_to_DB (database, map_source);} catch (Configuration_Exception exception) { System.out.println (ID + NL + "Unable to configure the PVL_to_DB mapper." + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_CONFIGURATION_PROBLEM); } catch (PVL_Exception exception) { System.out.println (ID + NL + "Unable to initialize the PVL_to_DB mapper." + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_PVL_ERROR); } // Set the modes. mapper .No_Op (noop) .Match_Mode (matching) .Unresolved_Source_Reference_Throws (unresolved_source_reference_throws) .Verbose (verbose); if (looping > 1) { mapper.Loop_Limit = looping; mapper.Loop (true); } // Map the PVL files into the database. if (verbose) System.out.println (ID); String source = null; int records = 0; try { Iterator sources = PVL_sources.iterator (); while (sources.hasNext ()) records += mapper.PVL_Source (source = (String)sources.next ()); } catch (Configuration_Exception exception) { System.out.println (ID + NL + "Configuration problem while mapping source:" + NL + source + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_CONFIGURATION_PROBLEM); } catch (PVL_Exception exception) { System.out.println (ID + NL + "PVL problem while mapping source:" + NL + source + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_PVL_ERROR); } catch (ParseException exception) { System.out.println (ID + NL + "Math expression problem in reference while mapping source:" + NL + source + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_INVALILD_SYNTAX); } catch (Invalid_Map_Syntax exception) { System.out.println (ID + NL + "Invalid map syntax encountered while mapping source:" + NL + source + NL + exception.getMessage ()); String message;; if ((message = exception.Pathname ()) != null) System.out.println ("Map parameter name: " + message); if ((message = exception.Value ()) != null) System.out.println ("Map parameter value: " + message); if ((message = exception.Reference ()) != null) System.out.println ("Invalid reference: " + message); if (exception.getCause () != null) System.out.println ("Exception cause: " + exception.getCause ().getMessage ()); Disconnect (mapper); System.exit (EXIT_INVALILD_SYNTAX); } catch (Database_Exception exception) { System.out.println (ID + NL + "Database problem while mapping source:" + NL + source + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_DATABASE_ERROR); } catch (Unresolved_Reference exception) { System.out.println (ID + NL + "Unresolved reference encountered while mapping source:" + NL + source + NL + exception.getMessage ()); Disconnect (mapper); System.exit (EXIT_UNRESOLVED_REFERENCE); } if (verbose) System.out.println (NL +"Grand total records affected: " + records); Disconnect (mapper); System.exit (EXIT_SUCCESS); } /** Disconnects the Database of a PVL_to_DB.

    If an exception is thrown by the Database Disconnect the exception message is reported to stderr.

    @param pvl_to_DB A PVL_to_DB object. This may be null in which case nothing is done. */ private static void Disconnect ( PVL_to_DB pvl_to_DB ) { if (pvl_to_DB == null) return; try { pvl_to_DB.Database ().Disconnect (); } catch (Database_Exception exception) { System.err.println (ID + NL + "Unable to disconnect from the database." + NL + exception.getMessage ()); } } /** Prints the command-line usage syntax.

    Usage: PVL_to_DB <Options> -Map <source> <PVL source> [...]
      Options -
        -Database|-SERVER <server name>
        -Configuration [<source>] (default: Database.conf)
        -Set <parameter definition>
        -Loop [<count>] (default: 0, no looping)
        -Pattern Case_sensitive | Ignore_case | Regex (default: Case_sensitive)
        -Verbose (default: false)
        -Quiet (default: true)
        -No-op (default: false)
    

    PVL source:

    The pathname or URL of a file containing PVL to be used as the source of parameter values to be mapped into database field values.

    Map:

    The pathname or URL of a file containing a {@link #Map(String) map} of PVL parameters to database fields.

    Database server name:

    The Configuration file may contain connection information for more than one database. The information for each database is organized by {@link Database#SERVER server} name, which may be specified. If no server name is provided, the Database will attempt to use the default server specified in the Configuration.

    Configuration:

    If the pathname or URL of the {@link PIRL.Configuration.Configuration Configuration} file is not specified the filename "Database.conf" will be used by default. If the configuration file is not in the current working directory, it will be looked for in the user's home directory. The configuration file must contain the necessary information needed to identify and connect with the database server (as required by the {@link PIRL.Database.Database#Database(Configuration) Database} constructor and its {@link PIRL.Database.Database#Connect() Connect} method). These are typically the database server "Type", "Host", "User" and "Password" access values.

    The configuration parameters are used to resolve "nested" parameter references (see the discussion of {@link #Map(String) map} parameters for the distinction between nested configuration parameter referenes and source parameter references). In addition, if the configuration file contains a {@link #PVL_to_DB_GROUP PVL_to_DB} Parameter group, then these parameters will be included in the {@link #Default_Parameters(Parameter) defaults} list to be used when resolving source parameter references.

    Set:

    Parameters defined on the command line supplement or override existing configuration file parameters. Parameters are specified using the usual {@link Parameter PVL} syntax. N.B.: Spaces, quotes and other special characters that are part of the parameter definition must be protected from interpretation by the command shell. Parameters may have simple - typically single word - names or absolute pathnames including groups that exist in the configuration file or new groups to be created. This option may be used repeatedly to define multiple paramters.

    Loop:

    Normally only one mapping pass is made through the PVL source parameters. If a {@link #Loop(boolean) loop} count greater than 1 is specified then each PVL source parameters set will be mapped repeatedly up to the loop count or until no database updates occur. This allows sets of parameters having the same names to be collected and sequentially inserted into the database using the {@link #LOOP_COUNTER_PARAMETER} in the corresponding parameter reference.

    Pattern:

    PVL source parameter names are matched with map parameter references by using case sensitive comparison, ignoring case in the comparision, or treating the map parameter reference as a regular expression. Case sensitive matching is used by default.

    Verbose/Quiet:

    The verbose option provides a log of operations, as they occur, to the standard output. The quiet option disables the verbose mode. By default quiet mode is in effect.

    Unresolved_source_reference_throws:

    Any unresolved source reference will result in an exception being thrown rather than the mapped database field being ignored.

    No-op:

    As an aid in debugging complex references in map parameter syntax final database update operations will be disabled so that the results of resolving map parameter references may be viewed without affecting the database contents. This mode is implicitly verbose.

    Help:

    List a command line summary description and immediately exit.

    N.B.This method always results in a System.exit with a status of {@link Update_DB#EXIT_INVALID_COMMAND_LINE_SYNTAX EXIT_INVALID_COMMAND_LINE_SYNTAX}.

    @param verbose If true the usage is printed, otherwise silence. */ public static void Usage ( boolean verbose ) { if (verbose) System.out.println (ID + NL +"Usage: PVL_to_DB [<0ptions>] -Map [...]" + NL +" Options -" + NL +" -Configuration []" +" (default: " + DEFAULT_CONFIGURATION_FILENAME + ")" + NL +" -Set " + NL +" -Database " +" (default: configuration Server)" + NL +" -Loop []" +" (default: 0, no looping)" + NL +" -Pattern Case_sensitive | Ignore_case | Regex" +" (default: case_sensitive)" + NL +" -Quiet | -Verbose" +" (default: -quiet)" + NL +" -Unresolved_source_reference_throws" +" (default: false)" + NL +" -No-op" +" (default: false)" + NL +" -Help" + NL ); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/PVL/Selector.java0000644000175000017500000003122611742734277016220 0ustar mathieumathieu/* Selector PIRL CVS ID: Selector.java,v 1.8 2012/04/16 06:14:23 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.PVL; /** A Selector is used with a Parameter or Value wherever selective comparison of Parameters and/or Values is needed.

    The criteria are specified as the characteristics of the Parameter and/or Value to use in making a selection, and the combinatorial boolean logic to apply to the characteristics when comparing one object against another. The implementation must provide the definition of the criteria and comparison methods declared here. The Selector interface defines the criteria symbols:

    Parameter selection characteristics:

    NAME
    CLASSIFICATION
    VALUE
    COMMENTS

    Value selection characteristics:

    DATA
    TYPE
    BASE
    UNITS

    Criteria modifiers:

    ANY
    ANY_PARAMETER
    ANY_VALUE
    SPECIFIC
    PATTERN_MATCH

    Logic criteria (as test conditions):

    EQUAL
    LESS_THAN
    GREATER_THAN
    AND

    How the methods that test for match conditions on pairs of Parameter or Value objects can be controlled by combining the selection criteria. A Selector criteria code is composed of bit flags stored in an int variable.

    @see Selection @see Parameter @see Value @author Bradford Castalia, UA/PIRL @version 1.8 */ public interface Selector { // The selection criteria: // Java guarantees that an int is 32 bits. // Parameter selection criteria (0-7) /** Bit mask for the Parameter characteristics criteria. */ public static final int PARAMETER_SELECTION = 0xFF; /** Criteria to select a Parameter's name. */ public static final int NAME = 1 << 0; /** Criteria to select a Parameter's classification. */ public static final int CLASSIFICATION = 1 << 1; /** Criteria to select a Parameter's Value. */ public static final int VALUE = 1 << 2; /** Criteria to select a Parameter's comments. */ public static final int COMMENTS = 1 << 3; // Value selection criteria (8-15) /** Bit mask for the Value characteristics criteria. */ public static final int VALUE_SELECTION = 0xFF00; /** Criteria to select the data of a Value. */ public static final int DATA = 1 << 8; /** Criteria to select a Value's type. */ public static final int TYPE = 1 << 9; /** Criteria to select a Value's integer number base. */ public static final int BASE = 1 << 10; /** Criteria to select a Value's units description. */ public static final int UNITS = 1 << 11; // Modifications to the selection criteria (16-23) /** Bit mask for the criteria modifiers. */ public static final int MODIFIERS = 0xFF0000; /** Criteria to select any characteristic. */ public static final int ANY = 0; /** Criteria to select any Parameter characteristic. */ public static final int ANY_PARAMETER = 1 << 16; /** Criteria to select any Value characteristic. */ public static final int ANY_VALUE = 1 << 17; /** Criteria to use specific criteria matching. */ public static final int SPECIFIC = 1 << 18; /** Criteria to use pattern matching. */ public static final int PATTERN_MATCH = 1 << 19; // The combinatorial logic to use (24-31) /** Bit mask for the criteria logic. */ public static final int LOGIC = 0xFF000000; /* The ANY_XXX bits are only used in the return values for the {Parameter,Value}_Criteria_Match methods to flag when the match was due to ANY condition. */ /** Criteria to use equality logic when comparing values. This can be combined with LESS_THAN for <= logic, or GREATER_THAN for >= logic. */ public static final int EQUAL = 1 << 24; /** Criteria to use < logic when comparing values. This can be combined with EQUAL for <= logic. */ public static final int LESS_THAN = 1 << 25; /** Criteria to use > logic when comparing values. This can be combined with EQUAL for >= logic. */ public static final int GREATER_THAN = 1 << 26; /** Criteria to use logical AND when matching with multiple criteria. Note: When this flag is not set, OR logic is used. */ public static final int AND = 1 << 27; // For exact match. /** Criteria to select an exact match of all Value characteristics. This is the same the "DATA | TYPE | BASE | UNITS | AND | SPECIFIC" criteria combination. */ public static final int VALUE_MATCH = TYPE | BASE | UNITS | DATA | AND | SPECIFIC | EQUAL; /** Criteria to select an exact match of all Parameter characteristics. This is the same the "NAME | CLASSIFICATION | VALUE | COMMENTS | VALUE_MATCH" criteria combination. */ public static final int PARAMETER_MATCH = NAME | CLASSIFICATION | VALUE | COMMENTS | VALUE_MATCH; /** Gets a description of the Selector. */ public String toString (); // Access selection bits directly: /** Sets selection criteria to the specified code. */ public Selector Criteria (int criteria); /** Gets the current selection criteria code. */ public int Criteria (); /** Gets the current Parameter selection criteria code. */ public int Parameter_Criteria (); /** Gets the current Value selection criteria code. */ public int Value_Criteria (); /** Gets the current selection criteria modifications code. */ public int Modifiers (); /** Gets the current selection logic code. */ public int Logic (); // Parameter criteria: /** Enables or disables the Parameter name criteria. */ public Selector Name (boolean mode); /** Tests if the Parameter name criteria is enabled. */ public boolean Name (); /** Enables or disables the Parameter classification criteria. */ public Selector Classification (boolean mode); /** Tests if the Parameter classification criteria is enabled. */ public boolean Classification (); /** Enables or disables the Parameter Value criteria. */ public Selector Value (boolean mode); /** Tests if the Parameter Value criteria is enabled. */ public boolean Value (); /** Enables or disables the Parameter comments criteria. */ public Selector Comments (boolean mode); /** Tests if the Parameter comments criteria is enabled. */ public boolean Comments (); /** Enables or disables using any Parameter characteristic to match. */ public Selector Any_Parameter (boolean mode); /** Tests if any Parameter characteristic will match. */ public boolean Any_Parameter (); // Value criteria: /** Enables or disables using the data of a Value as a criteria. */ public Selector Data (boolean mode); /** Tests if the data of a Value is a criteria. */ public boolean Data (); /** Enables or disables the Value type criteria. */ public Selector Type (boolean mode); /** Tests if the Value type criteria is enabled. */ public boolean Type (); /** Enables or disables the Value integer base criteria. */ public Selector Base (boolean mode); /** Tests if the Value integer base criteria is enabled. */ public boolean Base (); /** Enables or disables the Value units description criteria. */ public Selector Units (boolean mode); /** Tests if the Value units description criteria is enabled. */ public boolean Units (); /** Enables or disables using any Value characteristic to match. */ public Selector Any_Value (boolean mode); /** Tests if any Value characteristic will match. */ public boolean Any_Value (); // Criteria modifiers: /** Enables or disables using pattern matching for comparisons. */ public Selector Pattern_Match (boolean mode); /** Tests if pattern matching is to be used for comparisons. */ public boolean Pattern_Match (); /** Enables of disables specific category matching. */ public Selector Specific (boolean mode); /** Tests if specific category matching is to be used for comparisons. */ public boolean Specific (); // Criteria logic: /** Enables or disables using equality logic for value comparisons. */ public Selector Equal (boolean mode); /** Tests if equality logic is to be used for value comparisons. */ public boolean Equal (); /** Enables or disables using less-than logic for value comparisons. */ public Selector Less_Than (boolean mode); /** Tests if less-than logic is to be used for value comparisons. */ public boolean Less_Than (); /** Enables or disables using greater-than logic for value comparisons. */ public Selector Greater_Than (boolean mode); /** Tests if greater-than logic is to be used for value comparisons. */ public boolean Greater_Than (); /** Enables or disables using logical AND when matching with multiple criteria. Disabling AND logic enables OR logic. */ public Selector And (boolean mode); /** Tests if AND logic is to be used when matching with multiple criteria. */ public boolean And (); // Parameter criteria match methods: /** Tests if two Parameters match according to the current selection criteria. */ public boolean Parameters_Match ( Parameter this_parameter, Parameter that_parameter ); /** Gets the criteria that were met, as a selection code, during the last use of the Parameters_Match method.

    Only those necessary and sufficient criteria that were actually met during the comparisons are set. There may be additional criteria that could have been met but were not needed to satisfy the match. */ public int Parameters_Match (); /** Gets all the criteria that are met, as a selection code, when comparing two Parameters.

    All current selection criteria are tested and those that are met are set in the returned code. */ public int Parameters_Criteria_Match ( Parameter this_parameter, Parameter that_parameter ); /** Tests if the names of two Parameters match according to the current selection criteria. */ public boolean Names_Match ( Parameter this_parameter, Parameter that_parameter ); /** Tests if the classifications of two Parameters match according to the current selection criteria. */ public boolean Classifications_Match ( Parameter this_parameter, Parameter that_parameter ); /** Tests if the Values of two Parameters match according to the current selection criteria. */ public boolean Parameter_Values_Match ( Parameter this_parameter, Parameter that_parameter ); /** Tests if the comments of two Parameters match according to the current selection criteria. */ public boolean Comments_Match ( Parameter this_parameter, Parameter that_parameter ); // Value criteria match methods: /** Tests if two Values match according to the current selection criteria. */ public boolean Values_Match ( Value this_value, Value that_value ); /** Gets the criteria that were met, as a selection code, during the last use of the Values_Match method.

    Only those necessary and sufficient criteria that were actually met during the comparisons are set. There may be additional criteria that could have been met but were not needed to satisfy the match. */ public int Values_Match (); /** Gets all the criteria that are met, as a selection code, when comparing two Values.

    All current selection criteria are tested and those that are met are set in the returned code. */ public int Values_Criteria_Match ( Value this_value, Value that_value ); /** Tests if the data of two Values match according to the current selection criteria. */ public boolean Data_Match ( Value this_value, Value that_value ); /** Tests if the types of two Values match according to the current selection criteria. */ public boolean Types_Match ( Value this_value, Value that_value ); /** Tests if the integer base numbers of two Values match according to the current selection criteria. */ public boolean Bases_Match ( Value this_value, Value that_value ); /** Tests if the units descriptions of two Values match according to the current selection criteria. */ public boolean Units_Match ( Value this_value, Value that_value ); } pirl-2.3.8/PIRL/Viewers/0000755000175000017500000000000012052546522014541 5ustar mathieumathieupirl-2.3.8/PIRL/Viewers/Animator.java0000644000175000017500000002560711742735302017171 0ustar mathieumathieu/* Animator PIRL CVS ID: Animator.java,v 1.5 2012/04/16 06:22:58 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JLabel; import javax.swing.Timer; import javax.swing.Icon; import javax.swing.ImageIcon; import java.awt.Component; import java.awt.Graphics; import java.awt.event.*; /** An Animator is a JLabel that displays an animation of a series of Icons.

    Once started, an Animator will sequentially display the sequence of Icons that it has been given. The rate at which Icons are displayed and the number of animation cycles is controllable. The animation sequence may be stopped and started again from where it left off, or restarted from the beginning of a cycle.

    @author Bradford Castalia, UA/PIRL @version 1.5 */ public class Animator extends JLabel implements ActionListener { private static final String ID = "PIRL.Viewers.Animator (1.5 2012/04/16 06:22:58)"; /** Constant to specify an indefinate number of animation cycles; i.e. forever until stopped. */ public static final int INDEFINITELY = Integer.MAX_VALUE; /** The default number of animation cycles: INDEFINITELY. */ public static final int CYCLES = INDEFINITELY; /** The default initial delay before animation starts: 0 milliseconds (immediately after start). */ public static final int INITIAL_DELAY = 0; /** The default animation interval: 1000 milliseconds. */ public static final int INTERVAL = 1000; /** The minimum animation interval: 50 milliseconds. */ public static final int MINIMUM_INTERVAL = 50; private Timer Animation_Timer; private Icon[] Icon_Sequence; private int Max_Icon_Width = 0, Max_Icon_Height = 0; private boolean Adjust_Icon_Location = false; private int Icon_Index = 0, Cycle = 0, Cycles; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_TIMER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs an Icon Animator.

    @param icons An array of Icon objects to be animated. @param cycles The maximum number of animation cycles. Use the {@link #INDEFINITELY} value to continue the animation until stopped. @param initial_delay The initial delay, after first starting, before animation begins. A value <= 0 means no initial delay. @param interval The interval between the display of each Icon in the animation sequence. A {@link #MINIMUM_INTERVAL} is enforced. */ public Animator ( Icon[] icons, int cycles, int initial_delay, int interval ) { super ((icons == null) ? (Icon)null : icons[0]); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Animator:\n" +" " + ((icons == null) ? "null" : String.valueOf (icons.length)) + " icons\n" +" cycles - " + cycles + '\n' +" initial_delay - " + initial_delay + '\n' +" interval - " + interval); if (interval < MINIMUM_INTERVAL) interval = MINIMUM_INTERVAL; if (initial_delay < 0) initial_delay = 0; Animation_Timer = new Timer (interval, this); Animation_Timer.setInitialDelay (initial_delay); Animation_Timer.setRepeats (true); Animation_Timer.setCoalesce (true); setRepeats (cycles); Icons (icons); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Animator"); } /** Constructs an Animator using all of the default Animator parameters.

    @param icons An array of Icon objects to be animated. */ public Animator ( Icon[] icons ) {this (icons, CYCLES, INITIAL_DELAY, INTERVAL);} /** Constructs a disabled Animator with default Animator parameters. */ public Animator () {this (null, CYCLES, INITIAL_DELAY, INTERVAL);} /*============================================================================== Timer */ /** Starts the animation.

    If the animator was {@link #stop() stopped} in the middle of an animation cycle it pickes up from where it left off.

    Use restart to start the animation from the beginning of a cycle. */ public void start () { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Animator.start"); if (Icon_Sequence != null && Icon_Sequence.length != 0) Animation_Timer.start (); if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Animator.start: isRunning " + Animation_Timer.isRunning ()); } /** Stops the animation. */ public void stop () { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Animator.stop"); Animation_Timer.stop (); if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Animator.stop"); } /** Restarts the Animator at the beginning of a cycle. */ public void restart () { stop (); Cycle = 0; Icon_Index = 0; if (Icon_Sequence != null && Icon_Sequence.length != 0) setIcon (Icon_Sequence[0]); start (); } /** Sets the maximum number of animation cycles.

    @param cycles The maximum number of times to cycle through the Icons. Use the {@link #INDEFINITELY} value to continue the animation until stopped. @return This Animator. */ public Animator setRepeats ( int cycles ) { int repeats = Cycles; if (cycles < 0) cycles = 0; Cycles = cycles; return this; } /** Gets the number of animation cycles.

    @return The number of times to cycle through the Icon_Sequence animation. For indefinate animation this will be the {@link #INDEFINITELY} value. */ public int getRepeats () {return Cycles;} /** Gets the initial delay.

    @return The intial delay time in milliseconds. @see #setInitialDelay(int) */ public int getInitialDelay () {return Animation_Timer.getInitialDelay ();} /** Sets the initial delay.

    The intial delay occurs before the first animation cycle {@link #start() starts} or is {@link #restart() restarted}.

    @param initial_delay The intial delay time in milliseconds. A value less than or equal to zero means no initial delay. @return This Animator. */ public Animator setInitialDelay ( int initial_delay ) { if (initial_delay < 0) initial_delay = 0; Animation_Timer.setInitialDelay (initial_delay); return this; } /** Gets the delay between the display of each Icon in the animation sequence.

    @return The delay between the display of each Icon during an animation sequence. @see #setDelay(int) */ public int getDelay () {return Animation_Timer.getDelay ();} /** Set the delay between the display of each Icon in the animation sequence.

    After the animation sequence is {@link #start() started} the next Icon is displayed after the interval delay. Because the time can not guarantee its accuracy, especially when the display component must be rendered during the interval, the interval may be longer than expected at times. In general, a longer interval is more likely to result in a smoother animation than a short interval.

    @param interval The delay between the display of each Icon during an animation sequence. A {@link #MINIMUM_INTERVAL} is enforced. @return This Animator. */ public Animator setDelay ( int interval ) { if (interval < MINIMUM_INTERVAL) interval = MINIMUM_INTERVAL; Animation_Timer.setDelay (interval); return this; } /** Tests if the animation sequence is running.

    @return true if the animation sequence is active; false otherwise. */ public boolean isRunning () {return Animation_Timer.isRunning ();} /*============================================================================== ActionListener */ /** Performs the animation event at the Timer interval.

    If the animation is active the next Icon is painted. */ public void actionPerformed ( ActionEvent event ) { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Animator.actionPerformed"); if (Icon_Sequence != null && Icon_Sequence.length != 0) { if (++Icon_Index >= Icon_Sequence.length) { if (++Cycle > Cycles) { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Stopping at cycle " + Cycle + " of " + Cycles); Cycle = 0; stop (); if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Animator.actionPerformed"); return; } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Starting cycle " + Cycle); Icon_Index = 0; } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Displaying Icon " + Icon_Index); setIcon (Icon_Sequence[Icon_Index]); } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Animator.actionPerformed"); } /*============================================================================== Accessors */ /** Sets the Icon array to be animated.

    The animation is {@link #stop() stopped} and the {@link #setRepeats(int) cycle} count is reset to zero.

    @param icons An array of Icon objects to be animated. @return This Animator. */ public Animator Icons ( Icon[] icons ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Icons"); stop (); Cycle = 0; Icon_Sequence = icons; if (Icon_Sequence != null && Icon_Sequence.length != 0) { Max_Icon_Width = Icon_Sequence[0].getIconWidth (); Max_Icon_Height = Icon_Sequence[0].getIconHeight (); Adjust_Icon_Location = false; for (int index = 1; index < Icon_Sequence.length; index++) { if (Max_Icon_Width < Icon_Sequence[index].getIconWidth ()) { Max_Icon_Width = Icon_Sequence[index].getIconWidth (); Adjust_Icon_Location = true; } if (Max_Icon_Height < Icon_Sequence[index].getIconHeight ()) { Max_Icon_Height = Icon_Sequence[index].getIconHeight (); Adjust_Icon_Location = true; } } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Max Icon size: " + Max_Icon_Width + ',' + Max_Icon_Height); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Icons"); return this; } /** Gets the array of Icons to be animated.

    @return The array of animated Icons. */ public Icon[] Icons () {return Icon_Sequence;} /** Gets the current animation step.

    @return The current animation step. */ public int Step () {return Icon_Index;} /** Gets the current animation cycle.

    @return The current animation cycle count. */ public int Cycle () {return Cycle;} } // class Animator. pirl-2.3.8/PIRL/Viewers/tests/0000755000175000017500000000000012052546522015703 5ustar mathieumathieupirl-2.3.8/PIRL/Viewers/tests/test_Size_Limits.java0000644000175000017500000001356011742735304022050 0ustar mathieumathieu/* test_Size_Limits CVS ID: test_Size_Limits.java,v 1.3 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Size_Limits; import PIRL.Utilities.Integer_Range; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ComponentListener; import java.awt.event.ComponentEvent; public class test_Size_Limits extends JFrame implements ComponentListener { private static JLabel Limits_Width_Label, Limits_Height_Label, Component_Width_Label, Component_Height_Label; public test_Size_Limits () { super ("test_Size_Limits"); addComponentListener (this); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Limits: location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.EAST; location.insets = new Insets (10, 10, 5, 0); panel.add (new JLabel ("Limits - Width:"), location); location.anchor = GridBagConstraints.WEST; location.insets = new Insets (10, 5, 5, 0); panel.add (Limits_Width_Label, location); location.anchor = GridBagConstraints.EAST; location.insets = new Insets (10, 10, 5, 0); panel.add (new JLabel ("Height: "), location); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.insets = new Insets (10, 5, 5, 10); panel.add (Limits_Height_Label, location); // Component: location.gridwidth = 1; location.anchor = GridBagConstraints.EAST; location.insets = new Insets (0, 10, 10, 0); panel.add (new JLabel ("Component Size:"), location); location.anchor = GridBagConstraints.WEST; location.insets = new Insets (0, 5, 10, 0); panel.add (Component_Width_Label = new JLabel (String.valueOf (getWidth ())), location); location.anchor = GridBagConstraints.EAST; location.insets = new Insets (0, 10, 10, 0); panel.add (new JLabel (), location); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.insets = new Insets (0, 5, 10, 10); panel.add (Component_Height_Label = new JLabel (String.valueOf (getHeight ())), location); setContentPane (panel); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } public void componentResized ( ComponentEvent event ) { System.out.println (" Resized to: " + event.getComponent ().getWidth () + "x" + event.getComponent ().getHeight ()); Component_Width_Label.setText (String.valueOf (event.getComponent ().getWidth ())); Component_Height_Label.setText (String.valueOf (event.getComponent ().getHeight ())); } public void componentMoved (ComponentEvent event) {} public void componentShown (ComponentEvent event) {} public void componentHidden (ComponentEvent event) {} public static void main (String[] args) { Integer_Range width_limits = null, height_limits = null; for (int index = 0; index < args.length; index++) { if (args[index].length () > 1 && args[index].charAt (0) == '-') { switch (args[index].toUpperCase ().charAt (1)) { case 'W': if (++index == args.length || args[index].length () == 0) { System.out.println ("Missing limits for " + args[--index]); Usage (); } width_limits = Limits (args[index]); break; case 'H': if (++index == args.length || args[index].length () == 0) { System.out.println ("Missing limits for " + args[--index]); Usage (); } height_limits = Limits (args[index]); break; default: System.out.println ("Unknown argument: " + args[index]); Usage (); } } else { System.out.println ("Unknown argument: " + args[index]); Usage (); } } if (width_limits == null) width_limits = new Integer_Range (0, Long.MAX_VALUE); if (height_limits == null) height_limits = new Integer_Range (0, Long.MAX_VALUE); Limits_Width_Label = new JLabel (width_limits.toString ()); Limits_Height_Label = new JLabel (height_limits.toString ()); test_Size_Limits test = new test_Size_Limits (); test.addComponentListener (new Size_Limits (width_limits, height_limits)); test.setVisible (true); } public static Integer_Range Limits ( String limits ) { int min = 0, max = 0, index = limits.indexOf ('-'); String value; if (index == 0) { System.out.println ("Missing min value in " + limits + " limits."); Usage (); } if (index > 0) value = limits.substring (0, index); else value = limits; try {min = Integer.parseInt (value);} catch (NumberFormatException exception) { System.out.println ("Invalid limits: " + limits); Usage (); } if (index > 0 && ++index < limits.length ()) { try {max = Integer.parseInt (limits.substring (index));} catch (NumberFormatException exception) { System.out.println ("Invalid limits: " + limits); Usage (); } } else max = min; return new Integer_Range (min, max); } public static void Usage () { System.out.println ("Usage: test_Size_Limits [-Width [-]] [-Height [-]]"); System.exit (1); } } pirl-2.3.8/PIRL/Viewers/tests/test_Dialog_Box.java0000644000175000017500000000515311742735304021623 0ustar mathieumathieu/* test_Dialog_Box CVS ID: test_Dialog_Box.java,v 1.4 2012/04/16 06:23:00 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Dialog_Box; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class test_Dialog_Box { public static void main (String[] args) { final JFrame frame = new JFrame ("Dialog_Box"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JPanel contentPane = new JPanel (); frame.setContentPane (contentPane); JButton button = new JButton ("Notice"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {Dialog_Box.Notice ("This is something you should notice.");}}); contentPane.add (button); button = new JButton ("Warning"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {Dialog_Box.Warning ("I'm warning you!");}}); contentPane.add (button); button = new JButton ("Error"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {Dialog_Box.Error ("This is an Error!!");}}); contentPane.add (button); button = new JButton ("Confirm"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {System.out.println ("Confirm is " + Dialog_Box.Confirm ("Please confirm"));}}); contentPane.add (button); button = new JButton ("Check"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {System.out.println ("Check = " + Dialog_Box.Check ("Please check"));}}); contentPane.add (button); frame.pack (); frame.setVisible (true); } } pirl-2.3.8/PIRL/Viewers/tests/test_Percent_Bar.java0000644000175000017500000000511411742735304021775 0ustar mathieumathieu/* test_Percent_Bar CVS ID: test_Percent_Bar.java,v 1.3 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Percent_Bar; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Dimension; import java.awt.Color; public class test_Percent_Bar { static private Percent_Bar Bar; static private JSlider Slider = new JSlider (); public static void main ( String[] arguments ) { System.out.println (Percent_Bar.ID); JFrame frame = new JFrame ("Percent_Bar Test"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Bar: Bar = new Percent_Bar (); Bar.setPreferredSize (new Dimension (50, 15)); Bar.setForeground (Color.GREEN); location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (10, 10, 5, 10); panel.add (Bar, location); // Slider: Slider = new JSlider (); Slider.addChangeListener (new ChangeListener () {public void stateChanged (ChangeEvent event) {Percent_Changed (((JSlider)event.getSource ()).getValue ());}}); location.insets = new Insets (0, 0, 10, 0); panel.add (Slider, location); Bar.Percent ((double)Slider.getValue ()); frame.setContentPane (panel); frame.pack (); frame.setVisible (true); } private static void Percent_Changed ( int value ) { System.out.println ("Percent: " + value); Bar.Percent ((double)value);} } pirl-2.3.8/PIRL/Viewers/tests/test_Memory_Chart_Panel.java0000644000175000017500000000607211742735304023325 0ustar mathieumathieu/* test_Memory_Chart_Panel CVS ID: test_Memory_Chart_Panel.java,v 1.3 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Memory_Chart_Panel; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Stack; import java.util.Random; public class test_Memory_Chart_Panel { static private Memory_Chart_Panel Chart; static private Memory_Use Memory_User; private static class Memory_Use { private Timer Cycle; private Random Randomizer = new Random (); private Stack Chunks = new Stack (); int Total_Used = 0; public Memory_Use () { Cycle = new Timer (2000, new ActionListener () {public void actionPerformed (ActionEvent event) {Use_Memory ();}}); Cycle.start (); } private void Use_Memory () { if (Randomizer.nextInt (3) != 0) { int remaining = (int)(Chart.Available_Memory () - Chart.Allocated_Memory () + Chart.Free_Memory ()); if (remaining > (8 * 1024 * 1024)) { int amount = Randomizer.nextInt (remaining); try { Chunks.push (new char[amount]); Total_Used += amount; System.out.println (" Total_Used: " + Total_Used + '\n' +"Available_Memory: " + Chart.Available_Memory () + '\n' +"Allocated_Memory: " + Chart.Allocated_Memory () + '\n' +" Free_Memory: " + Chart.Free_Memory ()); return; } catch (OutOfMemoryError e) {} } } if (! Chunks.isEmpty ()) { char[] chunk = (char[])Chunks.pop (); Chunks.trimToSize (); Total_Used -= chunk.length; System.out.println (" Total_Used: " + Total_Used + '\n' +"Available_Memory: " + Chart.Available_Memory () + '\n' +"Allocated_Memory: " + Chart.Allocated_Memory () + '\n' +" Free_Memory: " + Chart.Free_Memory ()); chunk = null; System.gc (); } } } public static void main ( String[] arguments ) { System.out.println (Memory_Chart_Panel.ID); JFrame frame = new JFrame ("Memory_Chart_Panel Test"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); Chart = new Memory_Chart_Panel (); Memory_User = new Memory_Use (); frame.setContentPane (Chart); frame.pack (); frame.setVisible (true); } } pirl-2.3.8/PIRL/Viewers/tests/LAFs.java0000644000175000017500000000652511742735303017345 0ustar mathieumathieu/* LAFs CVS ID: LAFs.java,v 1.3 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.View_Locator; import PIRL.Viewers.Dialog_Box; import javax.swing.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Vector; public class LAFs extends JFrame { public static final String LAF_NAMES[][] = { {"Motif", "com.sun.java.swing.plaf.motif.MotifLookAndFeel"}, {"Java/Metal", "javax.swing.plaf.metal.MetalLookAndFeel"}, {"Basic", "javax.swing.plaf.basic.BasicLookAndFeel"}, {"GTK", "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"}, {"Synth", "javax.swing.plaf.synth.SynthLookAndFeel"}, {"XAWT", "sun.awt.X11.XAWTLookAndFeel"}, {"Windows Classic", "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"}, {"Windows", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"} }; private Vector LAF_Names = new Vector (); private JComboBox LAF_List; public LAFs () { super ("LAFs"); UIManager.LookAndFeelInfo[] info = UIManager.getInstalledLookAndFeels (); for (int index = 0; index < info.length; index++) LAF_Names.add (info[index].getName () + " - " + info[index].getClassName ()); for (int index = 0; index < LAF_NAMES.length; index++) if (not_listed (LAF_NAMES[index][1])) LAF_Names.add (LAF_NAMES[index][0] + " - " + LAF_NAMES[index][1]); JPanel contentPane = new JPanel (); setContentPane (contentPane); LAF_List = new JComboBox (LAF_Names); LAF_List.setEditable (false); LAF_List.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {LAF_Selection ();}}); contentPane.add (LAF_List); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } private void LAF_Selection () { String name = (String)LAF_List.getSelectedItem (); int index = name.indexOf ('-'); String message = View_Locator.Select_LAF (name.substring (index + 2)); if (message == null) { SwingUtilities.updateComponentTreeUI (this); pack (); } else Dialog_Box.Error ("Unable to set LAF -\n" + name + "\n\n" + message, this); } private boolean not_listed ( String name ) { if (name == null || name.length () == 0) return false; for (int index = 0; index < LAF_Names.size (); index++) { String entry = (String)LAF_Names.get (index); if (entry.indexOf (name) >= 0) return false; } return true; } public static void main ( String[] arguments ) { LAFs window = new LAFs (); window.setVisible (true); } } pirl-2.3.8/PIRL/Viewers/tests/test_Password_Dialog.java0000644000175000017500000000246011742735304022673 0ustar mathieumathieu/* test_Password_Dialog CVS ID: test_Password_Dialog.java,v 1.4 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Password_Dialog; public class test_Password_Dialog { public static void main (String[] args) { String password = Password_Dialog.Get_Password ("test_Password_Dialog", null, null); System.out.println ("Password: " + password); System.exit (0); } } pirl-2.3.8/PIRL/Viewers/tests/Makefile0000644000175000017500000000125511041262456017344 0ustar mathieumathieu# Makefile for Java classes # # CVS ID: Makefile,v 1.11 2008/07/22 04:42:22 castalia Exp # # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm JPATH ?= .:../../..:$(JCM) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = test_Blinker.class \ test_Dialog_Box.class \ test_Splash_Screen.class \ test_Memory_Chart.class \ test_Memory_Chart_Panel.class \ test_Stream_Monitor.class \ test_Animator.class \ test_Percent_Bar.class \ test_Password_Dialog.class \ test_Size_Limits.class \ LAFs.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Viewers/tests/PIRL.png0000644000175000017500000027225210634622567017201 0ustar mathieumathieuPNG  IHDRiH gAMA a0tEXtSoftwareXV Version 3.10ap, PIRL v2.3.10a (1/05)_cy IDATxy$}ʺ0\ H)J+K ]Z#Rx-ۊ][kIkI 3(6ɠHJ"! 8suOOO]w坕U?^UvNu`}b:++eOw{{W BP(M|@P(]u'BP(BP(AIP(`nW?S;9뺂 (va֪}&w{ ;7 ŝA{ghIfvvk_nbh淾זP Z_r0w{ ιs*n.rηw{a=w;wӻ=N*TT! * qNB0]ۙPvYNww]wzzzdz,-'R.v~<]Gʺ?XbsC;}k;3hTn}3`{'FsOZVTМ-BP(BP(>9(m}C<fYSO]!0SSEWjVyCux^طoRlePlv㏟:uܹszս3pcǏL_؞q/}(Cs]̻Gc}&~I Q&7y?v&S>A~*=7)?iٳgd<;L:'W7r_ߒP}͋=*3;Ϳ4K}ޭ/R<|~w?GW)#8p`b_?½P( ecPwR( 1;) Bԝ Bl N BP6u'BP(BP(AIP(ƠP( ecPwR( 1;) Bԝ Bl N BP6u'BP(BP(AIP(ƠP( ecPwR( 1;) Bԝ Bl f@Ȋ" wCRdv ->WZR(Qܕh<'Oɇv_GfVf؇Gb9L~Am/,u*Q .rֵ7~JŬ|?O/7ݐ5E)y$|/N-fXJS&NZx_P ;)w?gPdoo>3sn LVRƆM 3A|&.̽oNBeh<2&z[Ą,b1~vk`kŹ+Ls#k1B UPwR>AwҬD$2Ax'5!Z+Ρ-fbuq esМ-./L/|X>N=Or]#;9xC=AzٷԺzŌhg|uS( CN۳fsg4,k_Ȍj8 :FqV驺V?cRgyrBlN\i7v U/i9?/ǯrA'+I|"6.G!ZS๡A*bP|?{P( RJZ^{?k__raf˧߽9bTYe1' u/y1'LNZ.NЅ+ʝu'neִ޽ֿrEʫvx FN$F(Ƅ%t2G:Fsg^C7`Bl N]Lvӻ;,V/K~U @(0\k1s Y,7'~p;eBĄx )z_n!ε縞-f=1R(B Q0r"6vHAsIeԩ]YJPnu'2"*:qa,E$l7#Nrr(;ux+LP:IHL* ]"HTva1ֆL6\`)VBIl |!t^1/bCkAR^խ0BPFLJt ;Ƶp+&1(d:P(u'2:=e4AJގ>fC5)R(C2WE AmǸv1vb0Ǿ3k!zvX^$Y5]#pnH9PwR(#s_S;I7b_9zpt"fm~[ȊRԓHI!泙'q5> Ю:_9 ~B!0v*''a4V#uCNyGټg3}TOJ/ٸ?SvK3 )݅BA-:&Pcg4m[1cj'ĉg@oeJm(i]sSYƊrdS0_`4KOgc9-2BqJkKɬVVq߉mu3 3|?Sn(ˠ鶷/?CIln畘|JN-]b0LGGC}-!t$p,*8%% U(Cr/xpO$IL1IY(d[- (#,ە%q|  ΡhOIRRYI>,"+AVu̶(T %4/L L5oS q]'ྻ}7voֵ'繮{L ,@׵9y9d1LOXDCO?0y3&sȮUGgӚvPx୫΅7NWJK-9^lg\{ {Ӌ;yG6"ogv_ܳqt:IM$|?÷/2M48SV`vW/gIXMƞ7b P!H'zd3-l6h pV۵r}jO@&N"{Ĉ}s] $*2DzZRz~=}qCcZrŬ \Ol]Q(w,<Ț?K`RdiCH9] E2(.zC *N<7Y8LRj\Nrx`fc>G?[m8Vài۶$I<^oR纼 h۞r, r45ߴ3g 잩3W,--;~TS}_lݭQ(Od-I^7U8WN6m} 'p@8)n"wxYS{}IS۲d<7LN3S4Ε-)IDzD$Q劭Vdq-Ȓ@WG9nkF6]64UϨ;)mEO^J($G+g8I09U%hY_/Yf[w$f[ >MrFyӠskѝ 4 #:gϾCjnZh6 %!ūVoF4p_9]ϦIj8ɇhE eФ~o#R(t 6sV]nDVN<&55]}G 2 7_ivsqX+2Ip]2Nwt?3;M B`{AcYwғI0rb\}/ `cirR0V)WU橧?FIl-9 m CӶb 'h<{MRrԚcO~C7Yv^OFP7x^гAw_ަy8nyy}績TUUvIj\.IN$nki_v}-+ /躽 onJ~\y'NN誨Ȓwoa0쇛N=[I!_&AUrN=z8HuWg`:)"roFѨOض%opmǵrP:|C,--rE5Ѩ8?1OYm$7?5~:[-ΚW]^mw){tߚ}JԚ1Qc̄ŹfZޛIR,jl61;]DR45JBllhZJt2'Z(<_OM@6]093gm[a46c7- oA0n:u<:Bp )bq9M4TR`{bK]O%"kO=v|b,Lj̔kWKYsXA Wzv7&;bz\GS6 祗NoMQw !$ >k;$ d%h$|4&'淗Ly֦;)$4\mhxۖ%ɲk7NYM&Ge9綺՞ۯolYQ2JQSٌBdX G;tTWvxG - 7{V-voUyy. (~W߼TRz]eBڒe:[8}q|~== qbTJl6^qp B&ZkaA7rs߽0€7IZײz*I\ І$H; fU7$̳`ƅez v`w`ծncfx#$ܿ*^O1$;@ {ӋS۶|ض"QIl۞4uP|h4|QJLFA:',m5JNbS7DPnNbT,jzXkⶺg񀽡 (LF5A:h;Y"+JQOFV 跾d@, ׼$Iy(ۙ;X=9hMHBm-,Vnj2m;faVg3V&Wbv,_sdJ0.\ctM״셥nFtg qa0LQmOV $I}JId]IwϞ=qMh(Z*Us<_7|CmCf%Җi6Z4a9ԍo5akuMMe35IITX@Lx[ݩ\R>AX?ٲ2+>Qf0;3jFN X ׃g?V\{nɽPnNuI)Ii Iw/9NmKdғIڎh69OtI==۶hBV*s)SiۖV8n\(z}F+ _6l^m!k9gyIIMP?ѕ!AQ[ [saV+& b#6 JN5_;J:(fW)׭{.U~6fRV87sW*t=Ez)H`FFad') UB6Z'rRעi۪Yʬ ٺ\ʤ-gtzK{w;dE)+F憬`LE,9mحBS5uk~\ZDu A' lam2Q-+Xŷb{MS6%I ?|V%KO*A}\ |#SaضhWB5rQXr T͢Rmumkaq:J6 ;89QHk̅Ye2L+32AE5akU Eaɳ,rrOL12졠e4t'9if?8C#Su]\ut:+I|eZ- 4u Fg)kMevs,Tv/ϳ'x0, :،q<uMn2݊mYaի_˿ܦ ILFI֜D&36Რ1vP IDATךor$2^[Vf*-{Hݠ lncR;$$k:I߾1ģGbqkf^vl, @baVY/pjtA/c^\:g̅̚0pkLs{pF[.PR([KrKE`(OVlVn# 0Y0e8යKK6#f<\yɌ{"?4I)ITQ@ֹRQezA7Y(<."L0 #S5K{b X5hvր28 EF@,{/nbl6x&UE6,WKjS36q ggG':c=#%'}4`Nf$[۳1II* _g/9 #I!M3\ha˳FZD$\TcV cxΑoK 5(If.mW~B;< 'm[B u3{ua ` P5KaWv r N<-qd7&iqpS ;8WoBOɯ[TbDdJ!=tp-Lj8ߛ^XnCqunsPJq*0suS~%?䅾lq~ eXq$%h Pbo_CfTV?zh;U$AL`+Y ՞\ͫ^|ͯoSo!qzĩ$y$DL"\UCcucu]SU֫G$%KSzbjߡdRn'uN`Dw^y=23&50QǠ2 2z/DH{ )jx11fr^(љ|v|C3(gMq  t^Jݛշl8׭-ũqr9qK[!q9ށBF|?uO%쉩SZ.%8D%_,,N_]&\Ԩ8Ftgi:{'f)%9؎wDyEZ  @)ڭgg.ؖAV#Ss097*E' RĈl< Vݤ(,u:qkm,hu&~ȾK C(*JYLWp)R*ȈCʼnhI.jV@$ T]b_ iY}dq*+[{=_~hFP4͎#9ΰa yZ#Nn?AĈlX< :CXvkz#MtN?$m@ւǞ>u'qűfYJrwD8Tw7Nyk4E KwEje&Z"h7|$"$Ajy)/;`j1H( 1 ^YEc~/}nB Ƴ% 3q6)9MŹMθ:N>DxV$.%I8Q(e8դ.G RRӮ"gD &Vl8e)WO>S{Ҙ)rjLsE gTj?|č!FtK U%z<--x#hNAW}1Π3}FƒR^c$.WOd>)YQ&#+3 ,m;[Lt$-2e`D1*ΉT7*b(ŕk/A;݁8ߙ{Evq ,NH\`Q}Mr&Y( .Бhe'Hy @'k~_6)SLs"K;B~&3CG縆n= ALNGLtNcsWĔeK9yT/ۡ8.;}M]2Դ8GKbMw%̓" ?Dj'G!Lth^i}76y e889(8@Tbq.tf}Wp<[SXno8/[Ɯ:''&hʵr:vhO__}{oѦiw,#let<8rX&;LRcѨ1*h%i-D5fq$$ F+-NS?{ăS)B14- p $0 N9V,'ep @6!r- |ozbKY답o}fDwf? [(jTE=ؾV8grHla׽L  tiM#0YPcxۗNnf)vSgdK0 JuI k% >9bun5oTLtW7_۸0 z8^ SBϪ_򍗶c62;IdheaEȌEcа'}t%#qa[rN q+ Fmn(:E52a!n`ܚ[ e;E/i@VLP뉓Qq 7֚rb3IXnuO_mؕٹ+'?pq>o_I0di+Ul*09Ɠyf"?"+9E k.Ïa>\( "z8L7ɽ' $ ?+f܀p@Hя?P(D6#A'lmE **$Uu׉#׎ ?k1sW3{yҲcel"X#̥bi/~/zlBCpFm+5"h61(͆&#q@k)JaF56%eTPdVJ79"ʇ{=9[k| e;x6I^6J,ŴVނхJs~Ay=);&{V-&o_'۾8g6v0[-+ ZYINWV3Ÿ("X`t Kę̓u:NbT-N tEc̨o!so6~~ P([K._;IsÂNoD_#N֕uBTՋc=<"NMO咪R*λ,[Kǔ$qqcܹ֠q% s00}1AG>-E%lCOM8%)Jbקhx 厢HЙLE TQ N; 5v}q^O=U7fKKXcz]TyoҿHwi~Cp\qZ!ec2E %V$,j2gT-EcL_1 (W3xiP([O6gdK :o' |.|l"UDKDۗK+zv>ck4oYSU/Əie\׾f{#cq5EBd H\()i\6nR5]NHS JjLb4`yQ8 `Tef:m(;-$֪4gkrgeFK$h:DW? 9аǐܙiQSB@u$}:~EVȊƐ׳FJt>qQS87PEeƵpl~NP6OVDØO:ㆫ9q&:fd83-牓'9ckҊU_Ìwfu9ծ?=|I0.| iHYIvY _ZPWZ$H [.Q`z̝V6%K~"AJWlTnjjOc-1lr0+IUZNNQn}H %\1y^d&zd꬐M [I3qnJ(QJg< 4p5jtVbMwQ#sR6aל^j/N[> }y6`_LȜ"ĵcQd+"uSqKqm7p0\&}H'zΝ.qŬyǰ͚i{"|xC/ѝW뽸LFFv?ravW!n-{umQ4Fd䱧3ӤzJZd3g.;;#M᝖lCҶy򀨩d6TUhOPQ%OF $ :3k["PYPT)iM ⨬gO&D,}Rq?]6 ׌μ: #ށwaGdI81kEM b!/Q4)ZĬSNpz&38w$0ݏGc3𫘭\AMW8EP84%jno@mad}IO$;LBH }{%Kb`*'.֙ܐ>M bSbMUJ$fo0Bq&5F"$5cc@l7g*jcC/ѝ2˟Le^z6&ܸ:zJz "YI3GBL)9mVJyŶWZka iegaWU'HjtM\K[U⹼D̕i7, e艩q~&,K6a{;(S 7Ui 8&ECz⸆l:~gn&J#SV݈-coX}*fd5=OkH{H.BJGdʼnkđ'qr i@U }w{-P(C2HqŔg 7NC0R_o*͚i `} l۬"U";TDPvmlnkm7N!іDa @QصTs'κ Y׌q핅Ju;"egѝ'#ȶb$_!9DT!<_edhf@C!|X1lRa4wf:.E:~h/iW5]e&p2 N7kfR礼fJHnmdZ@G IDATXe ] H iݮ'Dig>Z1svEw5#7:>ZK ٹ4Eao͆XAFq |I4̠a, ?~QSNZg_fir{ʠT!*a; _I+'E ?TiW 4ݶ>]:)'㵬&D~8[yZ~땻TkOׯ߅QѷP32Y:6")1>4>KD%+87<"y݆4+Y} Ec[of_jiffmqϐ@$၇'IX;|ؾavḮ;t: IʅDMX݄C:;dawsOͦVkh:J)v]eV~9ʎ3ayEM:|-8HhvfEWZR^k6\N8E4̀y\OFQf+pmkIN_[MW(đ @0.i'Ȁfk2]{$ŽXv-xPvq 9he*$. smea*0nѹJ&lM9mvz|cWs%@fDwڹktF`S i *#퇤<0ʓ#__[B~BO#0kk$=kLF`DM% WAr!q9Eh6\igH@n(n:uBv iθ g*3ʼn5qsqvl,.5k~S"ΐn'˥ԃ噗kQv)JPH6Y.iT-+桓֢+̸"+͆~@Qde wL$ kAgV_;[ iQoY*ɶ^i9N[D\iHdzlJQ5s=1s ASɞuӈ3B2ڷBD!6pcD8"Nx[QmGg../'?5 Ϲ^tgnYm\؅N/ Yຜ &ZձV^'-oNEaEF"X3EUlv,A)/;c,e+nD" Z2/ܔέ䪿p?X̋&Q/xB`-;wl@YkZnOPXZiz^`Fb,u(u]_T(utضe,˞]/ +,,ˢ({$=:s/]v wooի׿GW߽#;x@!/rs/5uyv88>;77^?i}muao(Qbh-YKPK&BwҹϬPlEhV@tbw,Z|&rͧ) `ũt@7 \̓"BD˄,˦lf#N:.qMt %-L|Qήz҅ /2'۷o|[ߺz^=' J.p5EU51en 8MIP}lN<X0y,:ԏ۶f5.x :H$2RF=Ϭfcggi6n߾ҤeT`Y7 Lc%(@*JeI;=Ioi*]ןyD_q͛~y$Nu<ᔩ,[BnHe⊕9G:8Xu'.5,XWYMAl 855޻{O1C45od+g0ҨaD p8NYe8KQvr}g >#b ,:Lȇ*ҪAɆHǣ 9 ަ-۷hlM _|ek<Kin^-U-E)T]YifY VVpv\M %'nesyRa⋅ **Iy$+ؓ_?{w2zKPU Ӄ`In;$=U2F`i ?b>{Nt~[ /5A͒n#>[$B=Gnl;53KgBAK>o_n %p5TG'tN^vw>֎Kf,Clf,+F\ X]w/w\ZtvWsu\T}Ŷ\/Zhۦi6*CL]_$E繶pFQDP e,+NJR R3 [ESu]a` CހFL^+A-IB`HZ2bjmMґ\S?߰ K~>L(/&,9}!VGD9enB dS١0vQcҕ./Mo8zyIڃCl<Φ;MU"7:!S7v_}N~N:w h麱lCeKz Ga5Q;{Q,~?y+/",fcD%2;G!*LMCcb8(l)JI+4 "|R0B'l ARda2[NQTQ -O  P;)c!fC_?7o'ܓ~gаW {kʢ $=h0Z8^%o(*dt]f?lkх+/Y5,!LnrT۔3pk_wol|5`EŁ~r- f<6j0ƒ&W= Q e]&vocvtܸM[yJrB#(ƲMm ʅֲȨvϱcR:%E xdٛR<ϥ,.*T!1j%H(@K]F 9!xa:tEI{KO*AȄl貃0BhVU1'|_~_?I_Y@B4\?LpeAMfz-Ǻ L'o3JP[\n>s(841TF7p>('L6l|w~yDլ`0rI@hJjxlǨBSY[ AЎ)J./6zo$:mډAC^DVƲ `yXly:N;D;L~B]mIHF?I BKTQ: I!A OUőt]iET//e0[T-rM4v0VU$@a%JZRdNj! PsDu]mpj^o9NTKT_ÿ?ڷ^hǓ8c8~x|8#i::f nl>`e5CW;QIOޖ< HIfhȍƥ)iZ8ӴN1{c}_C&s^4k+ WeJ4%5ȓ 50k5ڱ<$X;Z-$':;CP⾷޸y=:|k{C0Ɋ!>Sr `(:L`nCzPSR)ꗿ3goݿ_NX7r PtSq[YATiI"D7WO-fwf|f3BB)rn@g&|,",_P PfQŸ_Si*ꂎ,R"]zOd5A8\}c="vӍ}mr =lPA QE9L|_&E"",sK?,ۺvlo}Ͼ?~ ć'jǣi[6Zry6%enan˝XbYFE@,0̐~ӗLS^l4lVlȕgE7Xcwfw=dS O7ţMp9_2餅,YLSLS*fOdYPːPWݙ7ˢ0x8kp<6<+!//6DN.zeC=d9VF޹ڟV@3%²B&K{2⎨ae34v;fmiHra$IL,Зi| 1dkػ 66T+)eylԥH\\lu;&1N,$kkXYin:AГPh_~^zb\'ƣ } ) ̳(T-n@x$-eCaa^0/'[!_f찌aPZ_q߼Q}$nGV]Z:]4a\_\8j/~iUϓw۲@x l DaaKaax%0 sE$Qeڝ9U'(H\KT@$vȎC0ihQv<}%%l,َheI=~/}_"8G/6,c w]/,lݽ˖mlY:\QE A9,|^luӵr'O[B͜iC <ٰpA;N[8dAxd0ts8y5CM>:C[![RfdF$SsS&RJ˧߳#bRai:TV V]X0=_\ 5T|0,Q e&$)M'qEQh&[p*4IY%=Cpq+ܿ+=TMc$ a,Q='kEe. m{{J͔2k+>i =:d7nIIeND$f! ,n߸ \y,?, V7]Ջ]_[]M,݉nRQ M,.'mT}g&8gLq>XL NI2k\߆ϸʺh%ބ\ KFدv@&P>'8Lgp!C&zF_2޾q7ynߡΝgx-iRlaie\dHI8ʙ$aW ^,|M4U%^&i /4偓$I%.EQ4Mb:Qj$ y0Ίzt-2KijQ:20si ݐwdYF`Tї_~{wN rO/sϜ'?kO_>߰, k_(,9e|vR~=^/^Xm騙mc覛e0zq&N r;h9t`a iQo>I\5fqpӒZ'%Xf4өSatPRxI GXH+~ѰDBJSR4(jaʜSAȺO0YȺO'2".cP]@%\r>3җo)^\tn XOk׾ejI쒨Uk_ `Y*IYF,g% 4M0Sa(JJ^a)_:`4al?Av;:=Ͼ0p4c 8'/&46$<"^ 1ņ_xbp]gϟ|ƦYo=~9J O[]/K90lUt]"i4KR <I61jPs'E!)q;G7` [ + + ՚ȚDŽȄoG 5˱RI&qY:upDC] 31σQyMnmt]_/UMS  ү.K0 B!@b EzpWf%[OI:y"DT)_v:EQ0i$0 {-i]6\Q4aeW_y-1=S-ALm*W&]IH۾+_o|a'qKfjw鵉DC@n*E>5V?mU.dU Ci-|#8QW膈b~XN{c)( 60+J7}4Id@Yٰ,s Z&km\28AP%I TQs`ʆ%f ӵՑ@ֆ >ulmϮ~:t/߬}_~_$e]БU((6t=aQ1 C d$Nhqq̋H176 ʜ HAFy,en 2UI$͐dYQrzb^?\7 F7Qfz CdkRR\̍ TN{p8Ԭ%^I [bʅ~!Y;ŗ-uS $dd`d ~ױT0VLF#MjYFy>HPw$-xO;wH=*oؿHW?ʈ_Y|,U$VGŴ?A$y4 \Q qP2y0]on#TH™:+XMtLmo_eh$I()ozw<)ȕ4y ԫF44sEI[XZUɌғ8#Ģc2߼}˜Io""닕 aY5nf6eyABa@tQ'O_= Mc9'k8ɣ&8)AL̔y" aDD4SlqJ~9HAluCg@uOQ7d0-F #hzBDu$|yD,~E8ﻌE1v8ULb2#eW pKNT geZ#I ;7NT3}!Pa8 :$QΥe('US)LgfLIÇY .Q7n>yJdk*KTN$CI1DxI1gN-6-Ĭq6yots4-Y!>$ !0)& r|h>QdxzNt8RRˋiǺH1jlcߐ,wʞ<1%Ac 5 Vrm,1m+-+b^̢P+5 /ƒXsD!N]:)a0BGAN' _n΅Rapj(II6^/U%&[w- P *fgSߝe q=e5˘Py0fL4M9MjZN%}'0 ,Op)zmPrG$wM--re@lJs6CǶx^L0 j}'1{s˦mJwcvϕ2[[8N q A40i5/gɵӋ3p۽(|ɴ ~0uy*/~MP Yx~@j)8pl-ͬnM7vFR[*xˆU"(6J6EY2JXkbO)uHb~7EBCB\QT!/V %eUSdB&5g8(ٰ|B\C#bg&[-0uF4udHI9RTl lYU6YFJ:#!r :h`ٲSf9TW ÐHh(xN9[\UQ=Zo+5'Vh?UpA51y1ʟ0O]F7?,* ,v 3QX1*|4vI$B%;`E阕gP|*/R]XZ= h ^A䷠2O1,o}0QWLK49+VD!*LF0 De֪D(IjΓ8BSO]<}^h_}w' {0Cn4$|K2vX, j*3I[wv/^Xm)0ɍi7ˁ@2-CFbMpPWLʜsymayZZ`z<Θ;Ӵ 1cݔeyD=kQelX*dz`YT="*-MSGc2Mn*jp)>z-ʻ Ȅ <̳1P*QI 2(\_}(^o~WGzq N MSJNV>1Iɋ G4\eIBRQzb+]մ-qF]I"j&E0p/$Lw`5t^ w+Uҹ(XF=I6.Lf)fi[OTM4\Z0 =jΓ~ڙ W^ a-?~SO7*LIʬhBe 2)9(JR4j[~ǽ n{oqu~Fz7Mgiۆ/CFMm P~@ׅph8rI A%0NNFė9 5  KDMxSB0/c?1ʺp(UbP5 ($hF jXqp/@KU UgUף*:&QՀZR# fY M}Y矻 ucd;T}zA %|gEIxH(os1&Onw0Z9)槉-Il <1K;PUl> `*-Pb]]+iSa[r-KuUjݐ5}2:'i22 Ar%RGy{D쬀3P' 0k"I<9Q)tee%c\oSJ4 M08W_X:9~xwS/wYxl#UՆA(K¹e^9D%UհjBX]|6%/[o p`eN#qo'iJ4SkN/V(af=L(4KԔ2nKHhb/MWEa_,tEQ܅2\t(#M5*Y}VsQ6vlUVYʪ%'<%hgBΥ#7"]y&J:j+"D9ԥA>T$ʎgRd0$U)dbFI`9X`0[<E àk"e}gu4`@G&c2C%vȰU~i4|yyh>heMsOu]qJ/%}2m%mJ'q_x3Ak._gânIg&&,_DU\?̒۽7~||ְm -3ILFpt$#j(M6|!Ywi9t-d󍛒"d%H`-(N܀z%O `NfPQ84Rk:f̀kTXEHIC SV'v<%tI4}eRv=$]7ŤZ~yE%IL0pa:TjbIZKIϝt1^/>6` !0Z*fYMtR}߲륧#J޽qA{|  \G KZ$kA0ɏ PMnRRTA(qҌf`XDc/ *Zuݡ4qЌjqfK tU$: |en' %^~ju x?(c'B]3 }1~(j vkmTDoNA)3v "iAXryKvЧk6 [|ߧk#@ZN[#`5|Ru^pKPG{UclҳA?UtLhD4Xn@&3$筚Ѝ8Nr|G6@nN>Nps8ee-0=TGfx;!d` ȓh8tB[ ۔H4Vj(U]K@ 6b#hqfh@ @ֳP%[࣬(I'ɈUPPB&7x&Hxfug&<-&͈M3T4 Ԥ$6-T|& JTNl*5Ӵ /풦EzAn\l6rP ˆ7oݥR"" dܮJ Co{a8ųY1x2=yGH-,'KKsmlI<|0\lhV=3dG)T dVSU1FZg 1Pehhj;UH[NiI e;5C8.]1 r r\ 4F=$ HFБηR4ͬV29Ay\ ~|E$l-c&`4q].Eu>ulIkCF%u8ϔl@I }`'ִF.9'q#J~#>sWVNk+[{I8/?{ U p66ՙNrǍ+7w׮pj 5o|Տ] btRikqH–/v)}.B n5Y3$ !8>tqܔEUW(+[AeM@5,)gVfY,r8c_/+5REQY%6zDIa:]CNz̄$N󟪩-YmkYQ Of*aˀSe Q+mlw O6%GJUB\jjW.Uo%!e#[3g].X-{PMQ,esm ~9!{y*:qed'#qi͑6:NJT93ryu".#M흝 .XeY93$v}"C2qe\hf68{3 -eO `a7'|G\^nd%~'ρq8'WsϜg3;Ԣ[{sqzʕӪ%I8 FEQfh}ÚKj= {HC_'z S(E%xOSLŒdp Qz\k<maØaR2 IJsE S&MƇp;Np aEQIsynwɷ&4M677}aE\& >Mʣ&EZ:Ж>3)T%j*5tJ㳋!ѱYvW_ſ8i,:vcqҡ-}° ;+=\vwi7]}{&!)b4ҳjn|Ps,p21-Ͳ1I 2~GNb~,g[8S$=P 1<ߝ+R3ҶROvo[~6diReʩZce e4!*cBZj! ]E-ˢ!NL޼9s|"0SWւ~ftjj%k&(N dZy{aO+644BJ0Ȥ^:Oj8yPXS *߃Ȣ |2)0* C̪EIg@ %bQvrj5<+F1U%GVL 2$e@|S|bYU"U McʓT_d É i"BAM1$N-_A<}z%W+͔5?dGsi #Z44n)D*E|eY32 KX^ \: ),# O>Dya(5:8%Idƹa3?NL}3@|_[? p\<1[ 0VmP-ZbJV򲒵QL{چx[9S@ޟOΐN0YtS dfM 5Qp2$ő(Rnh< ;yGCBO#Y܍p'0ޭ,?}M褠m >g*fe$ FXkds<@%A1E0 e,664zQde^7&| $TW(IhG!I4,`yZ|OQWjM_](`2S5"sݝcHؙ8>V.Ӌa86 I2v>Oca8c'\eGg=k[fgbr!)f=LZ+2J/[G%lY|z2~4Mҵ.ҬV 9o7ynWQEQϿK׏' 2d~d(I4A,CAiM1¬ܽ B7pXrM$liPq2$=-`Oc" ◌Pҝ?SYdqxl $5X#7PQ_v6 &"۬Ke!1NyߟYTꙨˋq콞䁞ADIJgpXU4i_R07} h燃b/gqq Zڼ"[-;Ѡ1 udTQ ΩƼs `&, L㚣 ɟ;pܓ0`ﶥ/? +@' ^ۛ'e0'LӶk$ʆeH2lbpbW.u@:0‚Kآ\*fMIT!6$ hcIűf 5%˸!*h v()莊V͢`(T&'BZ&Pat־h`'hR^46`=N\yNR d:udY g~+1mY:40eSNc ! oi{씙: NJZ ~ P @姙bOyQi%(]1zb#Jo{?zBVMy+?6\{cَUS'߰*DXoݻp/aa k&7I ʜ5@H0O3P|vRF9LFsK)$xQAflʪ ,Lw(1 Ҫ;40EHGFh*Yi* N0<-Ι "[y.ߵÓeƁ'|*ßhjk]8OZ$ 3"kdz!V?3 8$O NW{)$c'\QTQ }Q$FՑ uYγm+a POu3>Fcŋ?{o$Iz=U嗙UYYGWuOq8 D6"` *GȖ>a; Ci["%YAIQ4IQ&(H.3]]YY_o=311Guw>vnE -'Im,wB7d'L}:M9#N 4Jv@LM#p2 ԯH`l 1-dCw7|0Pl1޴zq4M/ėdqRw`S.е\0#vHIIӴɌp3I7K˕dA;JԤN(ih4|7]2`L7-pQaHגe_ތ8F#mrIU?9*e"P[?hҟ 2Hp(TJTtdYH,4I4u!NU)T3}y'\jz-NṚYj ,/t#/r:r CZa(Ԝ+i9Vte Bv`fy lh36l(Nl{YS3W' 8&9]>*<>݂g{kE 'a"^W%cz4kxDɑu,te&UAi`WNJRI:3A&譝G/[͈I~m(9qAuڎsVѐouqx-bzܹK߸_G Z+3Nbp6 Г0 tU/n_V"4 XB98zR*4-w: 3\8Z)&ڲjUwW B# &u < EqCэu6¬RW}ϭԁ\Ⓠz6L[WTMΥ^T$^{yQz$AacT&p)/X[I|yj ,@ʖ3IӘ1È1:[ם1uTK$c4tiJT-I&(,WOs;c݃1 ndm#lr*9_hnE.08870C?Q~_Wko܏ i TT$1hRE?"चm8>lfÖjXFH$l0LHlj"5 %3|ϑD;L(zVZQ"YrOZSlPv]e~ (T׋pjw:~稂\B?2l+Y!,B Ñv,hc"(6IL6Z6ë%?a_Ki_ HԺj;Ӊy> ݙf֕b$gJՕ]nڵa[mFߍE0YLcͤ:ї)8KKTN)0Bj )T=#c.p <ʋ5<29m: >&Q8j8}]?7#PdFuušcGUj9rU%p8P;|"4m9NQhI]~^Ru߈P"aOybc;2ġb͎5ػX.кKNH~C?OuݏW ]?.mYQjX=pZٶɝcAcqmkX6|9_XxVWMbI˶خx| lmyt؍nٟ7k/M7Lq@\wm<|kK@ߊ1Q1lc6+RfCeDf% M =H3h7;QTkLL$Bz.$a&H9V0Ӥn Qtx{VxӉ; 3q: :^Ol m\Rdu$,Akء: kl )Oz2w2 ťY(5d+¬ ꊡ[q멕C;{J%>Nu$iApGmdǒ{UqAI'J _ʼdZN۞(;go -Tޜ@mTݪ>Iqtd>CfήZ54KD:׍4b 3o|spo|e0D6Ri ]-y0aT{]7N f/u@2[%ZD2PCRSl#$4D9.J0Y뤧KNd~ fHsw5}" 2 -̀6BB)c.}fR{*u !dba0>[= R*ngc*sZc$ #0یsv*9rXEGIpo[YUbHqVă3BpxԛM]r,g7y A,5aTn|4E*ng9ҵi qYMTȓbS׏h\rc~nt$Lzf3"MlꚑůśDQ=\Q-EU#Ղh)-sũ_{$ƻ{o)LPא\wC1"  T3LbTv6 '܈K!G9{T yՋ""6v*P()0&G|¤'l 4Vjlb{ qyC7Z#ףF 3嚜$Kҽ父[.X7f -ꆒPrJYyC;%dVJTc{Vejܺq3O''I EUhuΫռsnMUWt ˱Lx=ic8,S@ï5. 8sb$bo yY fmzd4M$Sםr&xlu?4(kf%agFF ;0H82,0Ţ?#Jۭ70+BӠ:8SdH+{"*@KNչ& (budo+#o(0Sjh4o\vpw|)$-hꆒsCVN_7ɓsG7l!H}f:nk2XxM[\FXj݋Pg"vh[7USF3\UT9lb*/qWsTT5!$;ǚ ݂"t,jVr:-{3yOhMItp8ItΣ,Qzzԧo> तnَ 84xφn7rգ, ]<ºMu߸Ŋ%bA}j$[Zv3W4yj _s:w4Zv^ ^oJLTcИd[8 Iu:aBzR,Lh%W([, h5# p : 8-mEH2ndHA}S&i)$y(آCg L-iQ扸.QۖtWkDnZ/MGP:@4fZ:|q2KE8vǚWi>GߕjRohT߭8PuEEfAug 7]ѨIϜZݵSYH*01Aךp9Civpo~r\~rr3Z:tY D|m^X*6b/d{00Bl9qA8Gh gXvSbAs?O~}o>Bȸ)-i)B$c@A>.-=Û/sU eUF7Hlji. &݅0t삌^:Z8eH̔A >gO&fQEǹ Eyu? -sDua?H,S+dVFi\ad;Q84Fd#ED}_x4U̫#H-ڟr? 8 LEՕ4,v?c╫'t"$ri̯_E%dq(fA:X}y%/^Oab\[޵Hp'L)+3xo@@h6 Laa;iu]u#Sg eM]׍8XXx+?br_.7I,oeH28$;!KsՅ&xoZ-[M8 dшcjј(Sc|]Tzi3($Ćk8} DZ}MgǤBմ:&dG˱\?&=tF WQ棴8ì.ʫ<*h<]Ɲ -aaDq4VL~vHQ̣-II߬7a/( QBMBJKO~+6?Ҩ@E(B4fM\"bgͥCROf5iMnf0iV\. ftR9-&kBrW1C!erF}k/%|.bRT yRUJ*f]J;eywʘByT^ysNecM᏾[*%+ݏ/4se֛WkKc[Ep窮g>w#f&Sݴ3fr@M&mbg wn95ήZù ί%n>F_v5/<`Z,TwUh9El+,B PF8FH4"6F]q8kXqukq'>xKO%RӴN4 d[uMV Ls0 FY&P1!??md*ÝE´YN$cǻpCy]hh uh2J h&C@Be '$lq䅉 *>T~ַQ[bF> $pRVf2O#Ѫ qy"9m7ޓO&q6D!= G;Y/ f1϶ꊉ&EmeICQDA"뷏?wNOH:& j8 C{N"&A2Fm#p1+ۙ3z}۰?]Oyisj ݹ&{}zYQ}|isZ}p+nI<;{y^N[."fqY>'rwOiy_I U|vo>D5}*RܩiRO3*:Ii@-\ 0eShidpI*eߢꆩ5ڙ޲5J^پr~ηJ*:Tٔle1Vj _e.sY"{ G~M7( a(C[ -WNdᡞ88Rrgg6'3zi6)M'tNzͷI| }7/13pk?^~¹Ao|s8r, ׸oQ&F}1 Q| gۼ 3-Ѵv2!S[孽^w0sN?n•__ :;4jink˻xS(*"_TC-U/nåK_8-%&^Z̺,[2^@gQ^SѲl3/o\Q( ey}v3ZK|^@F"vO5Dハ;{{񒺳X5 >X]ٍS՝`FZk9zzPAKRPh%S%}'0cvڭHQ5,f܏aܬ8s0^N bSai9*0 _{ytpZZ,믬o(}+[XqsrNN&sX_$HlOod_yf(R^/VVY4Lgcg4o~z7vKOG0/hD^*)l@&b>0;}?&=>77~7glTCG˶, jyjLvR" mIdZM+N [MRu*Hil9y ,R40F YBT 9B#3uUݨUmdeT]C{t&K!3NULuoOpc!kԂmn$D[+ap5jM͘$0˯Q`8o['4Mo]-m=o&ryoPOT ӁQ2/|tbc;H:[i s 9ꘆ?o}s!Vm?{ii{fu,`M*;F p:0W>x<~>J/-U-nmjUй~хn?diVy=TpT@Y$k,IuZ?(ђy~XBXPf 8R0LdL AfQ'%Qs)k4䤔fCYmԷ&ZTU(*?HƻFC<m\_3e~ԑyDJ]?cBb5l4JʼnqO2';yklHBy6*c#XScLn+t?^k.SPL&.JF$ 9IBېEQym w]|A,Κւ}oH[xs%6CͪM}, v6o'_Z&|#puhm!pgSd T[p^iCLJ'ES,@_3jxk×+PЎC8V9srG) TmgbffU'{>_YKevwUv)zŽSF9|RAT'*~C u@棭fEO҅J8+g Sax Pk(s\ A^aWk#˹@Z Zz&m3,aa; W<: IDATK ",ʳFE-iseZ3^$Deiaj~IJkJ:OPN)Ŝꥨε>:wS_W heivߖ4Ƚ:w4;-z>x >+Q3 çn;g&vg@^FOfa,݋Xyk)lmGFc/o7`8I [_W'?e\uմ+ kέ+_kA؟0B),E6`<1+GJ,K=;> ?˪;ʛfGʶn{ ;[x~2%|! UR.,f1眀S1<)\VЍrB)zW=ˣh dTL̎X#~~ 2eHz-^:=I uc 0< [đUu=\oU_9n|nK i6TUJ[[|X;=˪u6{qq9ipjp^|9*a*[@FF}Ofu4ubz l | ZdIZ\v̓ɉ|׷jAҚ`_n^>q>82 :׻>W GA:M}{n8ܴ"5Ism9V0{ NX.R9JC\;h]Q4c?pʪ|W>~(?O˪0c۞`x/jn {Ů&nJIUBi4Z9Ыkʛy1 gZ:n)#U6l-2wa)2%-̀JQ P L@fY-^_NaN3a@Wvr6huԑȚvU/+cEwiȇ;o?4Y팒[ɨ6fRy~~Qc˚p8$VKiNup8dLn;d9\ C|>s΂"d7 IYƔ(1Xq΄L-]vq09~27W&Ab7$&3ͪ)+~S40z@Y玮]z3-xo|љC`<m$~k>1)z7@B}P/9۰8G枝V*UOt'VLeT! U-J9b3èBcȪ;!@spCLrg}|C2c#d)J2mbVQ1̨(KYEYJ`pܡKP^8EIYNpFx`{q2B7qyLSwiZ$k:J<*%".+C܏$ ^x`5Mm{e2qGy@l6ts4ډHRF~L4VX6kTH`_"Č*(yasא+vbg'U}ҥkFqD/VjJO>5zj}S_X\ѼnxB䁯G93t׍|u}eUY'x5z{! NcJ1yF /SQ,|1C6GΉ|'{+W R =廝2p;BɤRui4-qp43ä`~jPfۍ7nSHUGk1߽8suZ俶V(Qߒ5^퉽=1yB[t)*ҿ ?{ e}8akN,]׽PgfTLU/PN\6scĸB<ϓ\A iiLyXn*ɁUu)Wz'odP8Ng)'b 1%|L 0 z**؇+;GRdZbcRb4[C8679gDؙ-Fbݓt3k=s2Qk(봨Bu ~n+v?|a>4& G"Q-#f; Cqp,@>s㜵bIx`1 aM%:r}ꠖE >Sx.%K#m[1A,'Cpq,De$(ev2_dazQXuwso$F9\ eþ,(BYS3ffPrqw:JriAєp(*NAl+55]wBG)eqڣښkaa'h OvT}hZ1wX\7Cbgǝ_VC0ƻəC}bq+`5~k>FV u}`4﫜/4hm%&}޺ozֵή=ĹYY5O<,-U=vHp.UnpyVFKZOaӄHjʳpmC_GYE >;4NBͪ,%mJ+0hhܨi-aS2nqp$J%DM9]i($=ww>G\%v^+PUնA0i,!+f58ZZBS_8%q$,. qA0G1$pAчX]]η?ůr"T O<[g|xsc:Ztv>g/Hֻsln?B w~?y qq8'ׂa=1讽;Gzp{;K1?YߟL\ƅAP{O琢lǮ/fsQq8(\f0s!ly_>&d-t+眵[]b1h<޼X1'_;ֵd>d یX坩>_u[zc97'; h+V)URbr*5sJfuAz8_ h-"h,"hc!Sj1i JI&ʺܞh*t6ppݨA?aD줋 Mv #W&Z#e_ηy'B@KψcAf'#Au8D!XӶiy* 'm*7F9D%c!ޏ|i| 8Q>{fW]wjQI> ޏ" @]5ANvKSɋFb̀#smoh4OxnRƫN{8{FC{[;4xXp[ZtV{]72z~]ڎD5S_`CqōNoY 7bt(gĔLԱrGY P aZm~p27=?D[SчUVBss-Zi#$lQ r2L(0tB6EWaq4'gy$ eՒL2gM<ݝ '| HҮhW"n^d 5h- bg}^VёRφY{-%9Ov" ͪɟi1nz,McuP\$Mc!8NG@K+c;s9~xI ?MSD%2ʨ $nNZ=TE> fɣ5 5XSf~hԱdfb=]k;;ffm[k=d~V:Q՝8VjO="t|a]bKu?NVKݿZqtBn[p^4-thZ;Nc`4ܹn ξ}g(ghr{ry; +ΦZ;q/oxqjR->{B{ǟoEpRjwXy0㺪H 85 AIeaFZF^,+S)+E\Y Tj(RCt~_A"9F9LpW^"v,e}½Ɲ3\?}A, ۆ>C:ۓP'ɦW 9罺:q if;J -g8P71)f4E(y^gTUUgR)ٹK|rZ}}LAǙU`[CK^7&JW,}ɢOkXjje4ՙ_O&WD۴t5^`'-}Z{;ȅi&=gsi]aWw0|n] @%44ntbZN]oN]+@iEzso-14L}g/Luߵ|^uzA n򦃜.^qalGYŎi%\I4vԅ3gZ5oEAo^˪?Q>Vebi#%?< As1+KwPSU0H1Z-ʳ w259+ 55 ME - %j0ƺZ4A3ڎE_E?ʁh[fFS1H܊w U!eURYܽw鍀# Eٹ,5,eד%5'Uq KuKYy|GiA4PM*#>jp?N$tMӜϏQdT](_}? =ɜʶ&u3k_tt&^fruUy*&{~;3[M P{|ܲvZԦ8YkLV|tu&D1+ZltZuEu}*Cg>lm]7C}SC^1Mkxg/}zg |O}{QhxCqM#}q{鎓,tSq"F~=ח4d?pP\B0Tpx^l2L4xsBg)Ιh-iZz'`'YQS#S USղmE-Sd\0Ai / yd9Fן}㠽Z-:gp@x @Bfy $XIi(u _u: ȓ+ ˓ <])ՠD0Z~T˥~@ee eK%4M%jʻ3UqnRFQizĪѬVk)$p.ۥ yGHrTi8{@yDBχ i*D4E+6]Xԁu t9@n1ٙGF\ 32+[=3LCiծ$0BX¾am`˃/_ ?6lð JIW33]]KfV&d0 Cɬ:($"A2d9|g_9 0tz{`d}Vd·a\ ^Xf5ܸ1623x0?^1 ˶~QYg%3,Z r(ILFVyqީPS&cEE$ZI6F$d<33=kY}zNK ƒR۶AXƟz:"uͶݼ7~{;kXm'Q]Lb t@X N[ۗPNV\=>#[7[۶Ohx1Ag[hs^ZThR >-[L0֘i0{zBN„Y,W)|DƵ ~n{wͮA ! Qy`'ar/~ߴ8-78Z/}{yַge5v"X&ZOo0I8ZOr/n n]@b;ah巷ל0Um12W{?獞+41T(ƢڧUmrdWgUa-2YމvնQg^ٰN{Qc~{;8i_^IRt?qL|nut(M>>~۷n}릌"?G A0<ޓ 2HBۿ__7 2O%磊?^hykGGg?3Vʩ(ruE פCkfC\[ -aCMbUn IDATv\KSFrM48ȑֱ oܛ]oܹW|r!nE$nttvk@|/;wks8AǮS&v A8g`kf;3R 5%3_gwvy[4yq&s,Tݍj2UC)}yula2̴LOT$E9LJ~r,kRҮZKL]\R; B=*ʇV Ѭ6Q VX]ד$ ɋaU zW5Bk\>i~/Y56 fUr&4[Iގz[ {àBj^N_Kz;nIJ}b6y#AX9U4kɤE2Mau'N =}0,z ȁ h&]6Ah|uhuѓjUڶi"YhowRQDE4 uUVƶU; & ͭV.X6,t=+9箣nL?B+$Z(lVtp *Z&bdDL7q*1VúhzX=XPS[\2N*:̰yrZO$:[Re"X?v"DJ(KDԪ6\[P_,P.cm;˲iܫZLQT=.ւ~$Ǿ&ILLc~޼_%)l0$Ԛ2kΆ~Ӡ k9 bꍍH$|%FI8]w|Tfv0=)'XPfh\.:P6ZkSN&gz5mLO,>zYy痰;8=8x2Zvx[g®!)vw=?+inbб%9sIC%LM I\G3I;'e郕*8VQ'@\SU|󎁞HA9: ! z1N˟&K#s7aei2<0҉ ) Y&c~y$~P۶USJu֔jYTU4U]SꈐgĒEKO~_2M dP CgR`:Uϧ e杏WE4'mʼiinD}p?7OH4@/{+CKVwςh-/nljBbD RfS/ Ԇm~u!XGX+e F"i[/G4ƵFoOkG?z{:9?;n5I\[{>_NYI 3Mq]Z@yM3TQd$5m5mx'gUm;-ye4st߆qA}enB9w>(O ~@SJkhexT92+N܎j굚i Ӈ ,8/gBH'=eq(_]<)'{OŽ@\N՚Rm ҩq{I$+T]׉*P\.meZ5﷈e #y+Յy0*ܢ(YeԹK%O:^LfK^KY09ku6n}԰JU2$frl\zV4IgAt>B>}ǟ۸nmUB)leP8xXȲ`<΄ڲiʕY0&rQry s4 Qr˜Ͱákޥjn^nv#`Eeiٍi?QH'1KT\"ɆF~N DR&L NzdkISaT s2Ի^*a]jv,˪btﳻˆ,u,-˔/m1Kԗ O5iבu>-s}+WzzH=qTQ{8PTpdy2;'EKaڶk&c骟$aLcM&A$IHRK,EQ$loTEJ"(e&ee,he+Wddv0Ȳ,,(`x k/| 6cLNOӓfΛx)/~ޭKkmjl<+14AlB YD$J=]`Y$C-vR3q8=kӢeh.NI2J&ˍN&DaWsGD5vݷasYTkm_eRԅȊV)ެƛ;o͛KfWʠu/S-Tն_mYlY,oG3 k0֏Az0jJfoZ׵rY#5G 9yYufr-y0:[i&tuYk|ZIxKںuK9qHsk`z2mR-PVO 3˨B1Ev(Z}KYnQ)/;]qDDHdRyJ# Ԟ_ڶCe,t]'0^1Zme%OW:ۯ0m͊|ZU}ߣ߀*~&f_m8 Djk76001m!9CLGdIio=~tfw9۳o{k5d|PG^ws$:M]ױkee.ԣ2r&N™2pmNvU&~ 46z\wOC׵&vOA\X41~;џ)wvUUc#)Uҙ/$UT;~O?P|ȍMOY \S N @'R&jgG7 54%R U$ejoC*"a.Z"?9Ch< QKm֤k^\@GoOgk,4G8[[, [>ǩbF }P+CAw]yDHԓ0 e 7g&8dku$BeCўq8N//0H"*f]vUiv$U+A&fTC=w܋)<ϫζmTl] 6kEF+Ke\t(m۳_=}JVupV/h?Py6m{g8 Բ .ϵmRITnHҀT%`5B-qQ4iS㼠f8Ǘ.'iB51$z^՝_vX4#:R\8S|N5LMbk ٬ϗ-Vj[ [!+oZӧ'Qb-0B 14, ˩9Su]990htԈBK0:V[G;Ζ3$yV9Yw29ẽL<ո E¶$$p XNl'g*&iHH'1Nx|fuAcvn/əY)*wj{ʥ8%E:ܢ2U\x(ljD4K!.(n}xgu c13seөz(Nm&ѐ;}ȷ8=x=cơpN{Ml4=0Ӓ9Mi+ Ix]ٹk;5AԞ c˟f8}<vRgQLsxrv;`Ցm,LGu8;&e棱^v8=i 8cZ)hu6m ibvt|hx6Q(fլwOڸ6Alm|pyw͟o-/uj ϖL4ԼRykߋ5]Kr 5Nv504, ʘsιE9{x<%͆M>5K'($Z>8?S)գTtmso1?(>0x4f ^A&B"0}U\;$Yx.\͢IrNty\J=洊U$=ve*UNj\ ti͂h6z V- U+"4Nw~A vs5Z5]m.ROְ Մa գ=$i[[qY Ľ в' 68g8~r[,q8yWIxɬ|g&k[p4ƥ( O0=RĦaHa7=2fGh絁w;*v꺖ςD9Yͅ0&yYߺ[GG**eS xN0wF]KcU<[٢J@q%LLFYa T%nAB,O=Z .>ɂn~NX4ӰԜ/XȨk--DEm(]e`Y!UK ƕZ} !aۨCPSHA&RsO΃jiYdX8:4r*%Mgw=//TI~T>k\hZirl$rܲ63@[EM%mĶm*I{+o*&j6 WENJ㫄mqWT9?Huƴ ,ɥ7OÓDh5l`4My8"KrM%F\̏xGL$i5KnOa'r8w&tm6DiiX ^ImUF7Ibj]Js*^,Fjknҥ v*i$yyP<''a1@:{,A[kbi2Dzx<͏ƻ HbŌXtP7'!Hf]d,, VeK_8D\۶eY39+O=#R']3#_]0fN"R%nMQR}XAoIS 53a5+ O2+uyow 5u>f~/f λaeE›˩X}GE #X7n ~ɭf2*dV_kd ˸Ɠ+8Ÿjfs `Q2OMÚǷ0N3)EN?Adܿ5I'I ]߼BZՌAzڧ&i *M)_j~ia5-Τm*-g~B$2'A^t%I\u:V ͕OX 2 s4ɿwJY둷-<>Zx|\IsBNG+,q |zwzT{y$Mr K0k rxW͵T,DW$U@U$v;a8eht(DR̶ME a<ܘXiX2*2Vtlhྒ} AWElFi~~7v&dQk۝w'`?83xmtA$_{h4kh_*G3! iS J=χJY*™6Ҧuj5b^˄3`@&q_ȡm24|yޚl 1D릖hZs*Q.,A{G픦xfI¾D|QvyVˬ82o3ڒ`ϾTӳ˷xjȻ#~c?|F8ST˶]ҵZ^LkB^j؀Ve*=O9ۺm Ok^q!B`TP.Z|iY24kjLUXTTXq@ΑFRM3OH<.eXԼg֎>9xZ59%QGP* #{L Y2g8 "%٥qQ$*dGk,H!=f{u0Ʋ<[0NiT9Wz`YƊ,gI<38/y{dMhz|4 ~x45~mB̵ZG@V4IttKײTiXD,vsgOva,fA Hn]n`-\^HƭM%ϲzW۟dSmsfJhQ4GZBw37=m6"fSλ9oxIQNg 1-*鬜Jb.D\ȃaVT7$we]AʴO$ZDQ> p^&iF2L? 9`_5\rDiAqj5LL 4}S1Núu-jw\Hu(iӭCtAWgO>yN\-Vs6z%KdX)Dl0] XJN33s=J*.$`BXIߝzT Q`̴-&" D:"j7׵/_ޚ&n2Mf,'&x O'o\mm Z{'ﹶ ᵛl|pghdz;:>t4œ#Ok];q\I,6|j,pқ3vُaw3C kr+,4e2IӢ՘kj,&ir k*=4vc|׺of(Ζij*TJ*\ETga\ZV(ˉm~&mbU3Zw O\nq~j0e9OҘ6$!Ɩf>O&ž(1[fncK ̤a\s ϵersϬ2ȗv?k>@`2:LOdvr')!q!1f0,q4MR>3 tFǣCa Yqv_ɨ &n1`|?Eb* \̋bS }nttu V4>emʾ|~#5 gq,|t@+Uc׌MœxEun'c ˼U5CՅfcr{re  yjA6iUƋ4HfZ&|A((-ʹ4] 4*8Ygh9JδLif-B0BR/ 3*V =۟!m$TjcI""SP.DUuU*9z^RiIܢ_cXu eUF"U(+bCo1àmA0V՟h$Iٹ`yqRsԗB1 ,`4lܸ֞n 䜳p7̴^~3\ rh?(҇ ̭ÁhH,&DvF4t(quh&F!Knj"f6( $B-kv>Wi\kwŴVb6 kx}sl$|*Fp{*|V)vz5fz=|iO%'6WQzTk1"ղoq…8 4 oZ孽7 @jEXP2k5:3{qV6xֹi$dQ_gP^<׸ѵ:iȋqtVX@ `PnAZ|R}I/Z.&^xdYVeT; Sיm&Y|#@eG͚kJjWŪYvt:N8eRd&i{KJieQ m3I7!j>t0X&339@e(l$'ti)n;ハ|o;'I~Q;ad\z7;y8NOLilv(DO䇳_͸GZkq& ;)KCʆ祴4w-k>IܙooGOxoV'6^yp sL&0W`NY9WPzYekv εsاz/o {5b՞}ة>{uZD=Y?>;@gTԹACP⊠7 ebuDNtXl8:eb8d &#JUn6ļ.ԁ9-+9x]I<4 2qB)Tr+­AR vVƕH4^}l $ISU[jIXj;ɲNue)2aگe4˱U~I쳪!ҥ *vEƹ r!yz4ze35bް ǡp޹F2%;Enm_Mo F:g%/4mc @8ν(9 wvʊ,3DQ"K,߿{?qAнr4,i Xة (K(8v$'#7Ԣa޸zj1g6iV[W?Zmydw:os鶾=uWN[\h+8I9GvFv` Ϸ=C>owhg9QҬul *5-(ꉊ?⚺8zӹ;G-^+> 7[Q,-IHlGd%C嬣>datԅѡ*&#f{i!-I;K d8lYTxIIOl-KlZAt]Wǘt]*3U'*ZlZ9IwuŒ_ 2tͷw8|ovij$L?M&QfpbScL-`W-֙LLAiz XZ]tDM my}"O>wowm 0ak3ix5P[!^ }M>MĚŀgvs>Us|咥MVlU00v~?KUQϣxujGdujV9sƙ6AZY1+Ua @sg3GG2l "n㓏_w.=;ۼ=j3$Iye2msVc=ci+NþܨԦ`4J.H9o%Ubc3?4tF2(>9<8W TѠ-b'GViV*#9p>-=,&I}* :bWV1եOD[T,Z҈e۪č2[wJ ŹJ0C%yjyELnf%d"7)( cA? F6"baknL3@[~הj  ,YC%7 XjF61q d 0OC} (Qkޟ!;5-uu]$p}Za\(S$.|jTc5QL_TR&C"D|=*A\ ŅJscU4Ig0 +5aQ<7~ A+h #@54Z9]U6;tSqdzNDo]Zhrfa} ]nȲ϶6F ::4h.|Z`!gdv0 %u0>?bSV'ڞK~+ЕIRv@XbSIQ{}G1U*m٪!M@%XLxWPX{<8r4ov.S(rep# "w@i\_ŗ8s"Q_cH>zh2K}MR&+|fad6cV@uk{4v;\\7Sij6*b5 ?2 DN1r"mIyae+!s P,dٹvwknu]a |q&lpl$~;3 q` ?9 C)UU,.B".*DEJ+ͺ c@e>HPJ)`ҵ#j-FmG,DQ3XŘ /@tTWSpDvI"$[oݴV5Ck﹁c/Suf2 ]M,Kעhx&R!N7޵ƭ'{ - IDAT}n X,ňҵDO`a8⦜UՋG#g4j*!.m*YώM:S[/( iN]aX{SMKU)&srJ T߲>P0UT'8.DH)#H#F~M7aIFѤ')4 X%ٰ}ld|h[-dt @4^˶LþY+aqJ ;+烵tr8 3N|W !tl<͖Ou`jh>抗NBP)V0)Z"dn͉eT^^`_ZST "eIuzնGx/)d2IeBjP&.$IRw!=AaI HSS(|)s4dj*p1!'|MO%UCF)h9~CjpzJ@&B`e BY f|rġٰLhZM)c uY R> B2A0pcJGY|!3s7oZ{Zگe>H3B[0OD I*_3u;q[L@qJq>_B51p/;Re> eG|QѲ,]B=-V)L FWiH&GtDOuFtS{ 5-X= D[%M)V㜰x QY@QU>q{ ,pBCI7CW1Fo|mNw\쵇ESޜ>SnTwӉW3`{~#sѲ -E Y 52bpo_ @s3֕3>K*;&"l"*wy" #z*(fyI9Zɓwe=NRÌӓ J |%Օ+=.=,}>1 36'!'OCNudM`\հtrSN^EYc!%Nt [ <)y'O:ATFAwt.\2Q}b\efلF\*>ufe%YD"| sP%^n,EhІb8 53n4͚ƕj%|s鴁lgqkYЊ4N׶;oO@:a{eU;4tƽV d lY6%XiICQbCZ{U˘2f_W/664M pY%t*+` 4T|*Ğ"f#qŢg s>_Z{DOH!Y4q8=ijO1TC%yK2O0J  g|ڜ Ig*J8F xA2~%u*k0:_4d(Պ:)^;QWSySU<°!,X@]W ~B˽~8Syo4M?%\q&SJ%t!'rE[4F5U&L#Jx<,Ow>ʁY7MR5K}&\2VUWmpǏc}J2m}66C z[Zt:xR)=rkQX~ԩ@>ZwV[.}C# 3jg8N~w>Y }A]G2}tj>hcR-O޳5XXs+A#KOV~l|i{{§)iZ/JB$gJ͂jӹ&ZF \c/Ayٖ'pxI"\EP}, r dbfL S@#.7XK4Edhyp hc(.2j sD ^GAC}P-z//RD^DZO_\)@]X1z^Nb2A(ЋGۻq[3TZU*n(LȲ|6|E٪<f>ݻswU9;eadrF>жv^3~GYr[W6=Na0F#WꄱAlI(;Z!W$nTS0}oMgLM}S%NYôX%Pg5`9N܋?+X`gg^Ӆ Y#*x,`QBI y^gEUŲ- S (LRMYSApI!T"p3% B2#Ď41"H"d&mJ)űݻ;R-w@(| XIT;v/BȒGb R5bɗrU壹q!HÊ$棳#'B1RcA hM>d%Q8y'iCdqtCLYd/Tly)8MX'Vncb;"FQ e諸(D* E*I iV+FF)Ȓ{duQW⹣Y^T ~vo7BEU Wv-a"5*Z4KpgE7+y#G}@(Ǿ3{mVU#U/h^<_)*$U`<kq%e[{-\q0w:4 2?#6*׮-+1}kmtyӫlWoV:OT p¬&Ə3 QZ 7_ O޸| fYho]5Mb2fpО΅xll=$D5cl]Xw8|Ǿ8T-cCU,;#}]/lFnѹ/Zm_`w2҄&c>A)b$8__$I0L5V+Q/u:Ƌ-LxFhI:D -M0`0D^C{ ϝwgN=Ȕ 4kvJ@Ve|w̹\b[8>ymRf PQdY2,b06DQ8,ple8_&niW ݧmpV~G} 6)h#8409ٻoӢ۝$KIb#zN-ɤk`]`&T\7WJ|pƭ&޺uYV#'b`۝B426On7*,EWm)7]?LWIN'( Ǒ0붓|_/knMO$KM] 6[Κ [шtgGa/s;r NgG /U+Ƭ#MvlcMH?7nA_u̐&CL~S@l{Yo6cQ` pBQ z&.#\\g }@셩,@TS 5EU-OlmjM ŞP+G 7 t\!\8~qWlj!ǫ$S3Nq/pEE'D(2,p*OmuZ]WρW'McC& ϲJX/ʲ{H1#9YS@+ZuBC,߄zoQ UhPoGgS5SkӣQIӔIr1X ݩyɒԕDLŠ;ԍeU%.eeINsjhv27^}6OL{q!51^u}0)7Sy$"".< !bpylFE\3HZ=߇3 "gjl-M- 9QI* YTNtU!8=4ZTɋ-,/I 4GƋ/ F.(GM\:ݻ;OV jx+.#X$(r\OgepKc] h*2 <4Z_|v*'.Ȳ eMH݈R1FD%s͍*685-I6cПUs,`c=Jt>:UQ!s k-(bsEUR*泒aKYO,VeuiJMUrqX 9S:j"GxOF֒3՚j _!USp!u ޓhlvRz5jsWEyTLL>"OȺ_0*rBs%#\Ma$oRy$*@bIV|g)埇#=v7c/68R#k#K?4؉)^~dEw/ ZU4$D2B$JJ]'Mt]EX=vg"MtݴU agDY MeY'DahR4݂Zpq“Kβh?|Xߺ ~dnbK``S?ΫnOq] ?ýqֺ@S*­-ϫ 7`>EE,T;w'vWӒfnwn2fWaóǿOG³ΆTjo #Q RPRx>q#0,Zo)U͇xPygR$^>aRTE |?C)3:$vC%U\QNbu`qE<)x.7N52c*+n)etx$e)-KRtLK*س+<˄Z<_ͦSRQU==q0]ϧ ;MJeɲBXdaN\fܪպ3E3$!U'v߃Ps4,~8Ӄ{<޸q ] +3 y.^tgi%xrƥWU{wA+hqQU4Si7J+^J$VJVт7QjQ"oUҞ ns՚aB;?YWk;Y=XeG8đĒ \l3¢,M$:T%. 9b#Ս2U?ogWRwfqEch<9+A![~Q&`3`bQ4LnV3Oק$*(Pg?(l?r@Y\_RV0|屭S;z8꧛<;Szuwwám; L;LrM?SEEuǹM Ȃs|YNܹ.$ 0F1L(0J|h>]%`YԦO?z`-3he{?agY$L vǛC ȥF%VXo捭zSHNL'Q gպP;΄4vtt!$p 뉳q(GȲ#/>!9xJ;?qQ'Nן7o@Jc*{b~)~&68 +>CK IDATKJXd^&ԕ&M$gJͼΧi)E iTD&dHUI=/I%s >hF7 ad (a~p$dfqS|P RP4bpDMijI|y&["]?,U&w0۩5(¹S. `p(!j>Bb^X]7,z>_ zNEX tk.k~x/ Wڹ ʺ4Ĺ 8c3)ܺ`tۛw7߻yfDt'Goc;RlQ`4F^։~w(uIW6MBxR5INe^S:Fpep0wo@0D8`p :x:MMYRFQǝpQG4DSgGsIaMGOgɗ'z ӑ&8TTM?/ŷߗF_Dq ,r)>=Ljʉ u.,~Gѐ-hϺ,._ CX@֚E`eW ߗ61ݝu /w ҾqD1^ӴSY&y',N G|,p@մύ_R' "C:"yQs5cYQcz@EW']& #9ѡ#>~Ȣ'C=ry;w Ni%(sf Zbǥw߼he`bwʥJ{wjJʧGj/oW@4nJ*8{QcfM&Gq)y@?u;HZ-Tp[veN߸.o)avUHMY%_6<gn&nNh+Rق cZ,x,|9eo#iErfH$`&(M$Ngl:#yL@{HrkgT P19bydOOB}Mh{+"B7{ k gFeQm%OHY'ɏQUJfEQ_9Y!lq',`Ddv0u#ʏ_C $y>iڦ4 -kh뇍J^ӱ p!h[o_i3U" -R֧;G=; woQr/L}CV=X?*2\{Vn^|Www3^6&4RF"xS:Uƅ:9_mOܼnwſSGRXwVequc= c_31%~<& -p)*/C3;Sk"X%q(d޸-SXGQY&f6vDsYo^BWr#jb WPE9*'UpZUyX6U ޽ۛm*o.jb۬IYs zJg|H-߹P5Tk ާ?WL4Wmw*x!nv:{m;13e-WO+ dIC| Fylp%^b,F.X 澌‰8kz?O:u[P)m$?fHE5S\M<`4f0#y,|y}HD|4B/ݏ}#;Dw51 &Gx%68#ZPWXJq&'!IaO*92YËV%қЏ?e˶kI ztB%[EY5x;i,oƭqO>y|򞻱f*z^F^T{AP7]҃񟽞|JOכZwʹꓣiR N@FGH)5zO$*#xss3վ™[F4MLY~b:D{Brvf4x몈NhV*YV8p9 {dY"BBW9Lܪm#?L]. Dl2 βMLC)7 d=k7.~vs 4VlnVT=wxKZj&C(Y0lJ^yz-S6iv$^w.2ӲLa۝6K׷zs*,Wk$A^D$.;#VT}Ԝ-w1⾏R^)_.x,*V Ddg~=kAEy3gY640({4(1ݓ?=t]O m pYx3 Q9MKV.Wې^c SOǤWb"JBx-OQ]&EU5M5]֨kcIuXXPJѳs* PѠ 滷ݼ]/fDWB$-KыE0n\(YuCOݣD/ͽ捭hPȸPMYVdΜIX3utMI%_zmGw;P($o߾ e׏iNfl/(IⴻċO38;#p -[ v9dqQp@2V)in,ųe|絭k74]mt:;zf:z忹'<{Fj>z`c[Qgzb& {jKTmh/[wɞrh~zFrw{gjh#G0o)ώnނg# jb@XwKq75atSlUPeتɍ[,>z&sı8+>GHd\@GRF4)N~N;96D (g"n|MOgwqL"4 U`U=%Y5,qbI˲ѭz]vG˭Z)7w7ѕMCKk[?83?lN,l)k7e>tڍF3/]n= 2i:ъ^2g# D_۝rt0~΅ $oԕ{ .1hDޝ'O0&}F-K0П'5.:Y%|Or)7f^6Đك%.xpؠBhN1ŅmURu&* # w_5A&.A.4lOa8=[d ăpŗ"us]X`Q)PGϗ>Ʋ`/G~"@ʲE1BJh05bvtM5Owa7$,EWB0I,⛹g Y XHz͛%i`pyK 9c, Q[hFo1Љ˨EQԋ hz۔׍^7kJZ_6,d K f=y4g<|zeQI4*Zv|asnQ8ojzܬ ؗdV073jQWpuG:uJh\q}R/_tK6koZlU.=]\XO=S)ȗ1#l`*k&HE8DYO4 9A史MM\g(,,3sP,ao~`fNh%DEb݈ײid2RN-S`ꦪ5o 'xעZ-lR A޹6r_wKr87C${{Q6:[-& MPF])/}(Ї{>4}(&AvxUV7Jԍpf8#!q+Žh@(r8$ErsV7f۾0w ֫[o/Hrs4(F}Xox׋@FVu=m[qX֊L >U*s̘)S:6u@bDžjxVj^'i>Դq&r9Ͷ 3g_rxK/̥B*NY}Z}[s7 )@j͵FH^Yjn)ba4m$xvs? * |~,I?Rb|وv0 z&I9Ay(yĴ'l_] |_\qEQEcz%TAo\.iDbDU{!*8zN tI|| tJjpZB_mh,F)YmƾVێ:qnz>uZq*W,;?Hۃ(9MªE%9*9 9UiqRZg4g猔V*k[]ٲfaOO|'LCULlM?)),}rF,ϙ= )gA7"*MT-m0ϗEރz+[z_{z3䥳mo^HB0M˶vN6Mv( H>48EQ vҕMxVLP(1ƜGܣtTtZ 5vmA bln:TA1a#`T _8ӴiJtVdUݏ*E,]Dzã[]YuB`񟇛+7nEa5mعS<33̉ڭMLWd, >C;6(_qhaF5JySJcWׁ) FFn!sk׮'Ǫ 7j $*Vc$ cƝ\v17 @uUUm;OS67v:*v:ڹ+g|2?4k]7p%O;2][I=>Tyݐ8|A~?7S}6|Uޠ:LM2*HuZ4M@* fFP;6]~L@+ޚk@ޱX8x^T(M`@gͺ3'?2h$եZ3PyE469Sյս'.O޾k{tjDq{_/.mv"[vrTL~e~2Wm%sޖ?D %߾wKT+4qh$6їe)Rql̩X 8ȶ@?|{|JD<=DO*^BWɳeL|]X۟Bσ?={u鮜Xr?̜iZb}MKܚLѓ1Vo400 %y AS?~,©j66Bcd0Pt(*PG~"EPQi6ˋznk OlSJ9{6T:$[XQ#Z{QNM)ӧ\c`dZ}c}V34f+<ܬV|~peV{+;wWVW^vkFT<7^7zجXQ2\6n[P-)ZQ.Vȷ{ ޺M١xbmu2MvK᾿1;]ie)KMee;zTBݷ/p.րe"{rZ1)/c1ǬEgưvm ."RQ2nQhfUCQTJؤйkx}/jPt~1mJ+ U7 x% r5 _guP ,-mnm]V5 >CU!yA^o?h0ʥ7_`\~.q*ry:Qp;FnG{4V B "qؒ:94*b]*={]ftښ]ph(SktSz|,w*ʔUuKi dxIM|EstjQMS9'D^ 7auccdu{UjC'[F~r/N*leʬb`(L4y>ɓih'NeU R_ sZ Xյu hZyg(iUACN-2I^W7vܬ9^H8hNEl[/vZKGkJmeS>4l? ;^՘8'}Q2CeyL?gX\66cgZ\u3 ^! 41, D/\5ѝR(Y퇄Sף( ~k7vI>y466T!(fu-RJD {v&I]%;GaYjSŢ{`5XVX0vBn'$,_l OUO%Uzco$ib7 Ͼ^wJdhyjw֛, z*{ pʠ2 ˗v9=t+XX9[\*^)z(7C>O0p2{\kx+{~+;:ѝ68'&N" srZlVٗ_{oۖHq7Sc? C<|>o1 ۔91GLOaRPHg[0;;[̎xN?}9tV )\hV9‰?i8N;pJN1¶+>_أY叿Q8RA8_!rr tbmO33奥{hOnQzY_~0u_~GN(9 >߼;vN*N~pJ$/'];MKd#r壻8l0ɱ`;K?tdiiO>y6k׮Ho / t]wxag˳GOH; ΫW䞏p{Aq"4~te_Oĸ6TpvN){OpnoCnW~fo?!?n{ I/rҗB%DruwE D"H$CjD"H$CjD"H$CjD"H$CjD"H$4PdtIME%j^9tEXtCommentCREATOR: XV Version 3.10ap, PIRL v2.3.10a (1/05) gIENDB`pirl-2.3.8/PIRL/Viewers/tests/test_Blinker.java0000644000175000017500000000406611742735304021204 0ustar mathieumathieu/* test_Blinker CVS ID: test_Blinker.java,v 1.3 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Blinker; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class test_Blinker extends JFrame { private JButton Blinking_Button; private Blinker Button_Blinker; public test_Blinker () { super ("test_Blinker"); JPanel panel = new JPanel (); Blinking_Button = new JButton ("Unblinking"); Button_Blinker = new Blinker (Blinking_Button); Blinking_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) { if (Button_Blinker.isRunning ()) { Blinking_Button.setText ("Unblinking"); Button_Blinker.stop (); } else { Blinking_Button.setText ("Blinking"); Button_Blinker.start (); } }}); panel.add (Blinking_Button); getContentPane ().add (panel, BorderLayout.CENTER); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } public static void main (String[] args) { test_Blinker test = new test_Blinker (); test.setVisible (true); } } pirl-2.3.8/PIRL/Viewers/tests/test_Stream_Monitor.java0000644000175000017500000000557411742735304022565 0ustar mathieumathieu/* test_Stream_Monitor PIRL CVS ID: test_Stream_Monitor.java,v 1.8 2012/04/16 06:23:00 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Stream_Monitor; import PIRL.Conductor.Stream_Logger; import PIRL.Utilities.Streams; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JPanel; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.io.InputStream; import java.io.OutputStreamWriter; public class test_Stream_Monitor { private static Stream_Monitor Monitor = new Stream_Monitor (); public static void main ( String[] args ) { if (args.length == 0) Usage (); InputStream source = null; if (args[0].equals ("-")) { args[0] = "stdin"; source = System.in; } else source = Streams.Get_Stream (args[0]); if (source == null) { System.out.println ("An input stream could not be obtained for " + args[0]); Usage (); } Monitor .Write (Stream_Monitor.ID + "\n\n", Monitor.NOTICE_STYLE) .Auto_Style (true) .Auto_Style (args[0], Monitor.stdout_STYLE); final JFrame frame = new JFrame ("Stream_Monitor"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JMenuBar menu_bar = new JMenuBar (); menu_bar.add (Monitor.File_Menu ()); menu_bar.add (Monitor.View_Menu ()); frame.setJMenuBar (menu_bar); JPanel content = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; content.add (Monitor, location); content.setPreferredSize (new Dimension (640, 480)); frame.setContentPane (content); frame.pack (); frame.setVisible (true); Stream_Logger stream_logger = new Stream_Logger (args[0], source, Monitor.Writer ()); stream_logger.start (); } public static void Usage () { System.out.println ("Usage: test_Stream_Monitor \n" +" The source may be a filename, URL or '-' for stdin."); System.exit (1); } } pirl-2.3.8/PIRL/Viewers/tests/test_Splash_Screen.java0000644000175000017500000000446611742735304022353 0ustar mathieumathieu/* test_Splash_Screen CVS ID: test_Splash_Screen.java,v 1.5 2012/04/16 06:23:00 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Splash_Screen; import PIRL.Viewers.Icons; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.Timer; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class test_Splash_Screen { public static final String ICONS_DIRECTORY = ".", DEFAULT_IMAGE_FILENAME = "PIRL.png"; private JFrame Frame; public test_Splash_Screen () { Frame = new JFrame ("Splash Screen Test"); Frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel (); panel.add (new JLabel (Splash_Screen.ID)); Frame.setContentPane (panel); Frame.pack (); } public void Run_Test ( String pathname ) { Icons.Directory (ICONS_DIRECTORY, this.getClass ()); final Splash_Screen splash = new Splash_Screen (pathname); splash.Start (); final Timer timer = new Timer (2000, new ActionListener () { public void actionPerformed (ActionEvent event) { splash.Parent (Frame); splash.Duration (5); } }); timer.setRepeats (false); timer.start (); } public static void main ( String[] args) { System.out.println (Splash_Screen.ID); test_Splash_Screen tester = new test_Splash_Screen (); String pathname = DEFAULT_IMAGE_FILENAME; if (args.length > 0) pathname = args[0]; tester.Run_Test (pathname); } } pirl-2.3.8/PIRL/Viewers/tests/test_Memory_Chart.java0000644000175000017500000001506111742735304022204 0ustar mathieumathieu/* test_Memory_Chart CVS ID: test_Memory_Chart.java,v 1.4 2012/04/16 06:23:00 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Memory_Chart; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.Box; import javax.swing.Timer; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Stack; import java.util.Random; public class test_Memory_Chart { static private Memory_Chart Chart; static private Memory_Use Memory_User; static private JButton Annotation_Button, Seconds_Scale_Button, Start_Stop_Button; static private JTextField Rate_Field; private static class Memory_Use { private Timer Cycle; private Random Randomizer = new Random (); private Stack Chunks = new Stack (); int Total_Used = 0; public Memory_Use () { Cycle = new Timer (2000, new ActionListener () {public void actionPerformed (ActionEvent event) {Use_Memory ();}}); Cycle.start (); } private void Use_Memory () { if (Randomizer.nextInt (3) != 0) { int remaining = (int)(Chart.Available_Memory () - Chart.Allocated_Memory () + Chart.Free_Memory ()); if (remaining > (8 * 1024 * 1024)) { int amount = Randomizer.nextInt (remaining); try { Chunks.push (new char[amount]); Total_Used += amount; System.out.println (" Total_Used: " + Total_Used + '\n' +"Available_Memory: " + Chart.Available_Memory () + '\n' +"Allocated_Memory: " + Chart.Allocated_Memory () + '\n' +" Free_Memory: " + Chart.Free_Memory ()); return; } catch (OutOfMemoryError e) {} } } if (! Chunks.isEmpty ()) { char[] chunk = (char[])Chunks.pop (); Chunks.trimToSize (); Total_Used -= chunk.length; System.out.println (" Total_Used: " + Total_Used + '\n' +"Available_Memory: " + Chart.Available_Memory () + '\n' +"Allocated_Memory: " + Chart.Allocated_Memory () + '\n' +" Free_Memory: " + Chart.Free_Memory ()); chunk = null; System.gc (); } } } public static void main ( String[] arguments ) { System.out.println (Memory_Chart.ID); JFrame frame = new JFrame ("Memory_Chart Test"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Memory chart: JPanel memory_chart = new JPanel (new GridBagLayout ()); memory_chart.setBorder (BorderFactory.createTitledBorder ("Memory Use")); location.fill = GridBagConstraints.BOTH; location.weightx = location.weighty = 1.0; memory_chart.add (Chart = new Memory_Chart (1), location); Memory_User = new Memory_Use (); location.gridwidth = 4; location.gridheight = 3; location.insets = new Insets (0, 5, 0, 0); panel.add (memory_chart, location); // Right side buttons: location.gridx = 4; location.fill = GridBagConstraints.VERTICAL; location.weightx = 0.0; location.gridwidth = GridBagConstraints.REMAINDER; location.gridheight = 1; panel.add (Box.createVerticalGlue (), location); Annotation_Button = new JButton ("Annotation"); Annotation_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Change_Annotation ();}}); location.anchor = GridBagConstraints.SOUTH; location.fill = GridBagConstraints.HORIZONTAL; location.weighty = 0.0; location.insets = new Insets (5, 5, 5, 5); panel.add (Annotation_Button, location); Seconds_Scale_Button = new JButton ("Seconds Scale"); Seconds_Scale_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Change_Seconds_Scale ();}}); location.insets = new Insets (0, 5, 5, 5); panel.add (Seconds_Scale_Button, location); // Bottom row: location.gridx = 0; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = 1; panel.add (Box.createHorizontalGlue (), location); location.gridx = GridBagConstraints.RELATIVE; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.insets = new Insets (5, 5, 5, 5); panel.add (new JLabel ("Sampling Rate:"), location); Rate_Field = new JTextField (String.valueOf (Chart.Rate ()), 3); Rate_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Change_Rate ();}}); location.insets = new Insets (5, 0, 5, 5); panel.add (Rate_Field, location); location.insets = new Insets (5, 0, 5, 5); panel.add (new JLabel ("seconds"), location); Start_Stop_Button = new JButton ((Chart.Rate () == 0) ? "Start" : "Stop"); Start_Stop_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Start_Stop ();}}); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 5, 5, 5); panel.add (Start_Stop_Button, location); frame.setContentPane (panel); frame.pack (); frame.setVisible (true); } private static void Change_Annotation () {Chart.Annotation (! Chart.Annotation ());} private static void Change_Seconds_Scale () {Chart.Seconds_Scale (! Chart.Seconds_Scale ());} private static void Change_Rate () { int previous_rate = Chart.Rate (); try {Chart.Rate (Integer.valueOf (Rate_Field.getText ()));} catch (NumberFormatException exception) {Rate_Field.setText (String.valueOf (previous_rate));} } private static void Start_Stop () { if (Chart.Rate () == 0) { Chart.Start (); Start_Stop_Button.setText ("Stop"); } else { Chart.Stop (); Start_Stop_Button.setText ("Start"); } } } pirl-2.3.8/PIRL/Viewers/tests/test_Animator.java0000644000175000017500000000557211742735303021372 0ustar mathieumathieu/* test_Animator CVS ID: test_Animator.java,v 1.4 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Viewers.Animator; import PIRL.Viewers.Icons; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.ImageIcon; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Vector; public class test_Animator extends JFrame { private JButton Start_Stop_Button; private Animator Animation; private static ImageIcon[] Animated_Icons = new ImageIcon[0]; public test_Animator () { super ("test_Animator"); JPanel panel = new JPanel (); Start_Stop_Button = new JButton ("Start"); Start_Stop_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) { if (Animation.isRunning ()) { Start_Stop_Button.setText ("Start"); Animation.stop (); } else { Start_Stop_Button.setText ("Stop"); Animation.start (); } }}); panel.add (Start_Stop_Button); Animation = new Animator (Animated_Icons); Animation.setDelay (1000); panel.add (Animation); getContentPane ().add (panel, BorderLayout.CENTER); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } public static void main (String[] args) { Load_Icons (); test_Animator test = new test_Animator (); test.setVisible (true); } private static void Load_Icons () { int index = 0; Vector icons = new Vector (); ImageIcon icon; System.out.println ("Loading icons from the " + Icons.Directory () + " directory -"); while (true) { System.out.println ("Loading \"animation_" + index + ".png\""); icon = Icons.Load_Icon ("animation_" + index + ".png"); if (icon == null) break; icons.add (icon); index++; } System.out.println ("Loaded " + index + " icons."); if (index == 0) { System.out.println ("A set of \"animation_N.png\" files are needed."); System.exit (1); } Animated_Icons = (ImageIcon[])icons.toArray (Animated_Icons); } } pirl-2.3.8/PIRL/Viewers/tests/Icons/0000755000175000017500000000000012052546522016756 5ustar mathieumathieupirl-2.3.8/PIRL/Viewers/tests/Icons/animation_3.png0000644000175000017500000000126211604704333021664 0ustar mathieumathieuPNG  IHDRasRGBbKGD pHYs  tIME/:.*2IDAT8˥MHTQ{3w̽QoS3؏(&A ҬZ"6FDA-mj ki-J(BA t&Ѭi&[8#)-<9y}ldBB$EhI5+%+%b(R 0WNcڏq *e@}qݶzn5C@W"@hztԮk%Z z 'n( ;.e@h38 $b)3;?p3rd] o}viVj 2(C%\e"eDɈ(i%dm\HڔWAVe%?Hi`-5"CQrVh+W#|oTTo-"e!料bk < '.7U7P-J,$ޱ8fxr7_ +w@M O_ p{MTS sƬ4M˖iҷ5MeDJ6^IENDB`pirl-2.3.8/PIRL/Viewers/tests/Icons/animation_1.png0000644000175000017500000000131211604704333021656 0ustar mathieumathieuPNG  IHDRasRGBbKGD pHYs  tIME/N%JIDAT8˥OHq?nsjMY5R @t ɋuAHtЋ <V¤?R&t{~uhDySfpJ^{t^ /B4K!R/Rq)PH+Rn+CJh:ZREK Kʞgo77i *01AZ$W ,lKZ&Gc8nԤR8.F+U`"?))<>֔@l=B$|g.欔5B`#FW9kk]<%(L_sWO3kJjBmq%IENDB`pirl-2.3.8/PIRL/Viewers/tests/Icons/animation_0.png0000644000175000017500000000133311604704333021660 0ustar mathieumathieuPNG  IHDRatEXtSoftwareAdobe ImageReadyqe<}IDATx|]HSaǟq}JkcM&]uЗt#o #( vs"(/Fx1 S[8s[8}us^y/ ix P4ηvtPk "-lAMj:g@FQ"62t <W gj N!y i!D',muyʭRo~!9DG%2U7us:F&oIE?sTҰԻ5j#(JVb@,{v2Z⮛:+-< \ s1`mMZJR9C <-NDR u([GN]3!Rimf'3Lc>5IENDB`pirl-2.3.8/PIRL/Viewers/Font_Selector.java0000644000175000017500000010237311742735302020161 0ustar mathieumathieu/* Font_Selector PIRL CVS ID: Font_Selector.java,v 1.8 2012/04/16 06:22:58 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JComboBox; import javax.swing.DefaultComboBoxModel; import javax.swing.JTextArea; import javax.swing.JScrollPane; import javax.swing.JLabel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.Box; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GraphicsEnvironment; import java.awt.event.*; import java.lang.Math; import java.lang.Integer; import java.lang.NumberFormatException; import java.util.Vector; import java.util.TreeSet; import java.util.Iterator; // For the Test stub. import javax.swing.JFrame; import javax.swing.BoxLayout; /** A Font_Selector is a dialog box for selecting a named font and its size and style characteristics.

    @author Bradford Castalia, UA/PIRL @version 1.8 */ public class Font_Selector extends JDialog implements ItemListener { private static final String ID = "PIRL.Viewers.Font_Selector (1.8 2012/04/16 06:22:58)"; // The original and current fonts. private Font Original_Font = null, Current_Font = null; /** The default font when no other default is available: SansSerif 12-point plain. */ public static final Font DEFAULT_FONT = new Font ("SansSerif", Font.PLAIN, 12); // Font sizes. /** The Vector of default Integer font sizes: 9, 10, 12, 14, 16, 18, 24. */ public static final Vector DEFAULT_SIZES; static { DEFAULT_SIZES = new Vector (7); DEFAULT_SIZES.add (new Integer ( 9)); DEFAULT_SIZES.add (new Integer (10)); DEFAULT_SIZES.add (new Integer (12)); DEFAULT_SIZES.add (new Integer (14)); DEFAULT_SIZES.add (new Integer (16)); DEFAULT_SIZES.add (new Integer (18)); DEFAULT_SIZES.add (new Integer (24)); } /** The minimum acceptable value for a font size: 4. */ public static final int MINIMUM_SIZE = 4; /** The maximum acceptable value for a font size: 256. */ public static final int MAXIMUM_SIZE = 256; // Sample text. private static final String DEFAULT_TEXT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" + "abcdefghijklmnopqrstuvwxyz\n" + "0123456789 ~!@#$%^&*()_+`-=[]\\{}|;':\"<>?,./"; private JTextArea Sample_Text = new JTextArea (DEFAULT_TEXT); private static final Insets TEXT_MARGIN = new Insets (5, 5, 5, 5); private JScrollPane Sample_Font_Panel; // Selection lists. private JComboBox Font_List = null, // Disabled == null. Size_List = null, Style_List = null; private static final int MAX_LIST_LINES_SHOWING = 9; private static DefaultComboBoxModel Font_List_Model = null, Style_List_Model = null; private DefaultComboBoxModel Size_List_Model; // Contols when selection lists are active. private boolean Selection_Enabled = true; // Selection list labels. private static final JLabel Font_Label = new JLabel ("Font:", JLabel.RIGHT), Size_Label = new JLabel ("Size:", JLabel.RIGHT), Style_Label = new JLabel ("Style:", JLabel.RIGHT); // Panel for buttons. private JPanel Buttons_Panel; // Optional "Apply" button. private JButton Apply_Button = null; // Minimum size of the dialog panel. private static final Dimension MINIMUM_DIMENSION = new Dimension (250, 125); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_SELECT = 1 << 1, DEBUG_LISTENERS = 1 << 2, DEBUG_ACCESSORS = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Font_Selector containing the specified font characteteristics selection lists.

    Note: If no selection characteristics lists are enabled, then the dialog will not appear when setVisible. The resulting object will still provide correct responses to all of its methods, however.

    @param title The String to be used as the title on the dialog box. If this is null, then the title will be "Font_Selector". @param select_font If true, a pull-down list of all available font family names is included on the selection panel. If false, no font selection is provided. @param select_style If true, a pull-down list of the four possible font styles - plain, bold, italic, and bold plus italic - is is included on the selection panel. If false, no style selection is provided. @param select_sizes A Vector of Integer values listing the allowable font sizes to included in a pull-down on the selection panel. The list will be numerically sorted and duplicate values will be removed. If the list contains two values and the second value is negative, then all values in the range [first,-second] will be included in the selection list. Any value less than the {@link #MINIMUM_SIZE MINIMUM_SIZE} or greater than the {@link #MAXIMUM_SIZE MAXIMUM_SIZE} is excluded. If the Vector is empty, or all of the listed values are outside of the acceptable range, then the {@link #DEFAULT_SIZES DEFAULT_SIZES} will be used. If the Vector is null, no size selection is provided (but the DEFAULT_SIZES will still be the effective selection sizes). @param default_font The Font to use as the original, default, font. If the default_font is null and there is a non-null owner, then the font from the owner will be used; otherwise the default front from the dialog will be used. If this fails to produce a non-null font, then the {@link #DEFAULT_FONT DEFAULT_FONT} will be used. If the size of the final default font is not one of the selection sizes, then the {@link #Nearest_Size(int) Nearest_Size} to the font size in the selection list will be used. @param apply_listener An ActionListener that will be associated with an "Apply" button. When the button is pressed the button will call the listener's {#ActionListener.actionPerformed(ActionEvent) actionPerformed} method. If null, then no button will appear; but it can be added later, if desired, by using the {@link #addActionListener(ActionListener) addActionListener} method. @param owner The parent Frame of the dialog. This may be null. */ public Font_Selector ( String title, boolean select_font, boolean select_style, Vector select_sizes, Font default_font, ActionListener apply_listener, Frame owner ) { super (owner, ((title == null) ? "Font_Selector" : title), true); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Font_Selector: " + title + '\n' +" font - " + select_font + '\n' +" sizes - " + select_sizes + '\n' +" style - " + select_style + '\n' +" default_font - " + default_font); if (Font_List_Model == null) { // Initialize the list models. construct_Font_List_Model (); construct_Style_List_Model (); } boolean select_size = true; if (select_sizes == null) select_size = false; else construct_Size_List_Model (select_sizes); // Set the initial font. if (default_font == null && owner != null) Original_Font = owner.getFont (); else Original_Font = default_font; Font (default_font); Original_Font = Current_Font; if (! (select_font || select_size || select_style)) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println("<<< Font_Selector: no selections"); return; } JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; Selection_Enabled = false; if (select_font) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Font_Selector: select font " + Current_Font.getFamily ()); location.insets = new Insets (5, 5, 5, 5); panel.add (Font_Label, location); Font_List = new JComboBox (Font_List_Model); Font_List.setMaximumRowCount (MAX_LIST_LINES_SHOWING); Font_List.addItemListener (this); Font_List.setSelectedItem (Current_Font.getFamily ()); location.insets = new Insets (5, 0, 5, 5); if (! (select_size || select_style)) location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Font_List, location); } if (select_style) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Font_Selector: select style " + Style_Description (Current_Font.getStyle ()) + " (" + Current_Font.getStyle () + ')'); location.insets = new Insets (5, 5, 5, 5); panel.add (Style_Label, location); Style_List = new JComboBox (Style_List_Model); Style_List.setMaximumRowCount (MAX_LIST_LINES_SHOWING); Style_List.addItemListener (this); Style_List.setSelectedIndex (Current_Font.getStyle ()); location.insets = new Insets (5, 0, 5, 5); if (! select_size) location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Style_List, location); } if (select_size) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Font_Selector: select size " + Current_Font.getSize ()); location.insets = new Insets (5, 5, 5, 5); panel.add (Size_Label, location); Size_List = new JComboBox (Size_List_Model); Size_List.setMaximumRowCount (MAX_LIST_LINES_SHOWING); Size_List.addItemListener (this); Size_List.setSelectedItem (new Integer (Current_Font.getSize ())); location.insets = new Insets (5, 0, 5, 5); location.gridwidth = GridBagConstraints.REMAINDER; panel.add (Size_List, location); } Selection_Enabled = true; // Sample Font: Sample_Text.setMargin (TEXT_MARGIN); Sample_Font_Panel = new JScrollPane (Sample_Text); location.insets = new Insets (0, 5, 0, 5); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panel.add (Sample_Font_Panel, location); // Buttons: Buttons_Panel = new JPanel (new GridBagLayout ()); JButton button = new JButton ("Cancel"); button.setMnemonic ('C'); button.setToolTipText ("Cancel selection"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Cancel ();}}); location.insets = new Insets (5, 5, 5, 0); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; Buttons_Panel.add (button, location); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; Buttons_Panel.add (Box.createHorizontalGlue (), location); location.insets = new Insets (5, 0, 5, 5); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; if (apply_listener != null) { Apply_Button = new JButton ("Apply"); Apply_Button.setMnemonic ('A'); Apply_Button.setToolTipText ("Apply selection"); Apply_Button.addActionListener (apply_listener); Buttons_Panel.add (Apply_Button, location); } button = new JButton ("Select"); button.setMnemonic ('S'); button.setToolTipText ("Accept selection"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept ();}}); location.gridwidth = GridBagConstraints.REMAINDER; Buttons_Panel.add (button, location); location.insets = new Insets (0, 0, 0, 0); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; panel.add (Buttons_Panel, location); Dimension dimension = panel.getPreferredSize (); if (dimension.width < MINIMUM_DIMENSION.width || dimension.height < MINIMUM_DIMENSION.height) { dimension.width = Math.max (dimension.width, MINIMUM_DIMENSION.width); dimension.height = Math.max (dimension.height, MINIMUM_DIMENSION.height); panel.setPreferredSize (dimension); } getContentPane ().add (panel, BorderLayout.CENTER); pack (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println("<<< Font_Selector"); } /** Constructs a Font_Selector with no "Apply" button, and using a null owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, boolean select_font, boolean select_style, Vector select_sizes, Font default_font ) {this (title, select_font, select_style, select_sizes, default_font, null, null);} /** Constructs a Font_Selector with no "Apply" button, and using a null default font.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, boolean select_font, boolean select_style, Vector select_sizes, Frame owner ) {this (title, select_font, select_style, select_sizes, null, null, owner);} /** Constructs a Font_Selector with no "Apply" button, and using a null default font and owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, boolean select_font, boolean select_style, Vector select_sizes ) {this (title, select_font, select_style, select_sizes, null, null, null);} /** Constructs a Font_Selector with font and style selections enabled, with no "Apply" button, and using the DEFAULT_SIZES.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, Font default_font, Frame owner ) {this (title, true, true, DEFAULT_SIZES, default_font, null, owner);} /** Constructs a Font_Selector with font and style selections enabled, with no "Apply" button, and using the DEFAULT_SIZES and a null owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, Font default_font ) {this (title, true, true, DEFAULT_SIZES, default_font, null, null);} /** Constructs a Font_Selector with font and style selections enabled and using the DEFAULT_SIZES and a null default font.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title, Frame owner ) {this (title, true, true, DEFAULT_SIZES, null, null, owner);} /** Constructs a Font_Selector with font and style selections enabled, with no "Apply" button, and using the DEFAULT_SIZES and a null default font and owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( String title ) {this (title, true, true, DEFAULT_SIZES, null, null, null);} /** Constructs a Font_Selector with font and style selections enabled, with no "Apply" button, and using the DEFAULT_SIZES and a null title and owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector ( Font default_font ) {this (null, true, true, DEFAULT_SIZES, default_font, null, null);} /** Constructs a Font_Selector with font and style selections enabled, with no "Apply" button, and using the DEFAULT_SIZES and null title, default font and owner.

    @see #Font_Selector(String, boolean, boolean, Vector, Font, ActionListener, Frame) */ public Font_Selector () {this (null, true, true, DEFAULT_SIZES, null, null, null);} /*============================================================================== Actions */ /** The method that implements the ItemListener interface to listen for list selections. This method should only be used by the dialog itself. */ public void itemStateChanged ( ItemEvent event ) { if (! Selection_Enabled || event.getStateChange () != ItemEvent.SELECTED) return; if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println (">-< Font_Selector.itemStateChanged"); Object list = event.getSource (); if (list == Font_List) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println (" Selected font " + Font_List.getSelectedItem ()); set_font_name ((String)Font_List.getSelectedItem ()); } else if (list == Size_List) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println (" Selected size " + Size_List.getSelectedItem ()); set_size (((Integer)Size_List.getSelectedItem ()).intValue ()); } else if (Style_List != null) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println (" Selected style " + Style_List.getSelectedItem () + " (" + Style_List.getSelectedIndex () + ')'); set_style (Style_List.getSelectedIndex ()); } } private void Accept () {setVisible (false);} private void Cancel () { Reset (); setVisible (false); } /** Resets the current font to the original font. */ public void Reset () {Font (Original_Font);} /** Sets the dialog to be visible on the display.

    Note: If no selection lists were enabled when this Font_Selector was constructed, then it will never become visible. */ public void setVisible ( boolean set_visible ) { if (isVisible ()) super.setVisible (set_visible); else if (set_visible && Font_List != null || Size_List != null || Style_List != null) super.setVisible (set_visible); } // Apply button: /** Adds an ActionListener to the "Apply" button. If no "Apply" button is present, one is added.

    @param apply_listener The ActionListener for the "Apply" button. */ public void addActionListener ( ActionListener apply_listener ) { if (Apply_Button == null) { make_Apply_Button (apply_listener); GridBagConstraints location = new GridBagConstraints (); location.insets = new Insets (5, 0, 5, 5); location.anchor = GridBagConstraints.EAST; location.gridwidth = 1; Buttons_Panel.add (Apply_Button, location, Buttons_Panel.getComponentCount () - 1); Buttons_Panel.validate (); } else Apply_Button.addActionListener (apply_listener); } /** Removes an ActionListener, if registered, from the "Apply" button, if present.

    @param apply_listener The ActionListener to be removed from the "Apply" button. */ public void removeActionListener ( ActionListener apply_listener ) { if (Apply_Button != null) Apply_Button.removeActionListener (apply_listener); } /** Gets an array of ActionListeners currently registered with the "Apply" button.

    @return An ActionListener array. This will be empty if there is no "Apply" button, or no ActionListener is registered with the button. */ public ActionListener[] getActionListeners () { if (Apply_Button != null) return Apply_Button.getActionListeners (); return new ActionListener[0]; } private JButton make_Apply_Button ( ActionListener apply_listener ) { Apply_Button = new JButton ("Apply"); Apply_Button.setMnemonic ('A'); Apply_Button.setToolTipText ("Apply selection"); Apply_Button.addActionListener (apply_listener); return Apply_Button; } /*============================================================================== Accessors */ // Font: private void set_font ( Font font ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Font_Selector.set_font: " + font); if (font == null) { if (Original_Font == null) Original_Font = getFont (); if (Original_Font == null) Original_Font = DEFAULT_FONT; font = Original_Font; } if (Current_Font == font) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Font_Selector.Font: font unchanged."); return; } String name = font.getFamily (); int size = font.getSize (), style = font.getStyle (), index; boolean update = false; if (Current_Font == null || ! Current_Font.getFamily ().equals (name)) { update = true; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Changing Current_Font name to " + name); if (Font_List != null) { index = Font_List_Model.getIndexOf (name); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Font_List selected index " + index); Selection_Enabled = false; Font_List.setSelectedIndex (index); Selection_Enabled = true; } } if (Current_Font == null || Current_Font.getSize () != size) { update = true; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Changed Current_Font size to " + size); if (Size_List != null) { index = Size_List_Model.getIndexOf (new Integer (size)); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Size_List selected index " + index); Selection_Enabled = false; Size_List.setSelectedIndex (index); Selection_Enabled = true; } } if (Current_Font == null || Current_Font.getStyle () != style) { update = true; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Changed Current_Font style to " + Style_Description (style) + " (" + style + ')'); if (Style_List != null) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Style_List selected index " + style); Selection_Enabled = false; Style_List.setSelectedIndex (style); Selection_Enabled = true; } } if (update) Sample_Text.setFont (Current_Font = font); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Font_Selector.set_font: update " + update); } /** Set the current font.

    If the font has a size that is not in the size selection list, then the {@link #Nearest_Size Nearest_Size} in the selection list will be applied to the font.

    @param font The Font to become the current font. @return This Font_Selector. */ public Font_Selector Font ( Font font ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Font_Selector.Font: " + font); int size = font.getSize (), nearest_size = Nearest_Size (size); if (size != nearest_size) font = font.deriveFont ((float)nearest_size); set_font (font); return this; } /** Set the current font name, style and size.

    The name must be an available font name.

    The style must be one of the Font class style constants.

    If the font has a size that is not in the size selection list, then the {@link #Nearest_Size Nearest_Size} in the selection list will be applied to the font.

    @param name The String providing a valid font family name. @param style A Font class style value. @param size The point size for the font. @return This Font_Selector. @see Font */ public Font_Selector Font ( String name, int style, int size ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Font_Selector.Font: " + "name - " + name + ", size - " + size + ", style - " + Style_Description (style) + " (" + style + ')'); if (name != null && Font_List_Model.getIndexOf (name) >= 0 && Style_List_Model.getSize () > style && style >= 0) set_font (name, style, Nearest_Size (size)); return this; } private void set_font ( String name, int style, int size ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Font_Selector.set_font: " + "name - " + name + ", size - " + size + ", style - " + Style_Description (style) + " (" + style + ')'); set_font (new Font (name, style, size)); } /** Gets the current font.

    @return The current Font. */ public Font Font () {return Current_Font;} /** Gets the original font.

    @return The original Font. This was the default font determined when this Font_Selector was constructed or when the {@link #Select(Font) Select} method was used. */ public Font Original_Font () {return Original_Font;} /** Sets the current font to the named family.

    @param name The font family name to be applied. @return This Font_Selector. */ public Font_Selector Font_Name ( String name ) {return Font (name, Current_Font.getStyle (), Current_Font.getSize ());} private void set_font_name ( String name ) {set_font (name, Current_Font.getStyle (), Current_Font.getSize ());} /** Gets the name of the current font.

    @return The name of the current font. */ public String Font_Name () {return Current_Font.getFamily ();} private static void construct_Font_List_Model () { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Font_Selector.construct_Font_List_Model"); Font_List_Model = new DefaultComboBoxModel (GraphicsEnvironment.getLocalGraphicsEnvironment () .getAvailableFontFamilyNames ()); } // Style: /** Sets the style of the current font.

    @param style A Font class style value. @return This Font_Selector. */ public Font_Selector Style ( int style ) {return Font (Current_Font.getFamily (), style, Current_Font.getSize ());} private void set_style ( int style ) {set_font (Current_Font.getFamily (), style, Current_Font.getSize ());} /** Gets the style of the current font.

    @return The Font class style value of the current font. */ public int Style () {return Current_Font.getStyle ();} /** Provides a description of a style value.

    @param style A Font class style value. @return A String describing the style. This will be null if the style value is invalid. */ public static String Style_Description ( int style ) { if (Style_List_Model == null) construct_Style_List_Model (); if (style >= 0 && style < Style_List_Model.getSize ()) return (String)Style_List_Model.getElementAt (style); return null; } private static void construct_Style_List_Model () { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">-< Font_Selector.construct_Style_List_Model"); Style_List_Model = new DefaultComboBoxModel (new Object[] {"Plain", "Bold", "Italic", "Bold & Italic"}); } // Size: /** Sets the size of the current font.

    @param size The font size. @return This Font_Selector. @see #Font(String, int, int) */ public Font_Selector Size ( int size ) {return Font (Current_Font.getFamily (), Current_Font.getStyle (), size);} private void set_size ( int size ) {set_font (Current_Font.getFamily (), Current_Font.getStyle (), size);} /** Gets the size of the current font.

    @return The point size of the current font. */ public int Size () {return Current_Font.getSize ();} /** Gets the size from the selection list that is nearest to a specified size.

    If the size is less than the smallest selection size, then the smallest size will be returned. If the size is greater than the largest selection size, then the largest size will be returned. Otherwise the selection size nearest to the specified size, rounded up, is returned.

    @param size The size to use in searching for the nearest size in the selection list. */ public int Nearest_Size ( int size ) { int total = Size_List_Model.getSize (), previous = ((Integer)Size_List_Model.getElementAt (0)).intValue (), current = previous, count = 1; while (count < total && current < size) { previous = current; current = ((Integer)Size_List_Model.getElementAt (count++)).intValue (); } if (size >= current) return current; if (size <= previous) return previous; if ((current - size) <= (size - previous)) return current; return previous; } private void construct_Size_List_Model ( Vector sizes ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Font_Selector.construct_Size_List_Model: sizes - " + sizes); if (sizes == null || sizes.isEmpty ()) sizes = DEFAULT_SIZES; else if (sizes.size () == 2 && ((Integer)sizes.elementAt (1)).intValue () < 0) { // Sizes in the range [size[0],-size[1]]. int size = ((Integer)sizes.elementAt (0)).intValue (), last = -((Integer)sizes.remove (1)).intValue (); if (size < MINIMUM_SIZE) sizes.setElementAt (new Integer (size = MINIMUM_SIZE), 0); if (size > MAXIMUM_SIZE) sizes.setElementAt (new Integer (size = MAXIMUM_SIZE), 0); if (last < MINIMUM_SIZE) last = MINIMUM_SIZE; if (last > MAXIMUM_SIZE) last = MAXIMUM_SIZE; if (size > last) { sizes.setElementAt (new Integer (last), 0); last = size; size = ((Integer)sizes.elementAt (0)).intValue (); } else if (size != last) while (size <= last) sizes.add (new Integer (size++)); } else { // Sizes list; sort and unique the list. TreeSet sorted_sizes = new TreeSet (sizes); sizes.clear (); int count = 0, size, previous_size = 0; Integer size_Integer; Iterator iterator = sorted_sizes.iterator (); while (iterator.hasNext ()) { size_Integer = (Integer)iterator.next (); size = size_Integer.intValue (); if (size >= MINIMUM_SIZE && size <= MAXIMUM_SIZE && size != previous_size) // Only add unique sizes in the acceptable range. sizes.add (size_Integer); previous_size = size; } if (sizes.isEmpty ()) sizes = DEFAULT_SIZES; } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Font_Selector.construct_Size_List_Model: sizes - " + sizes); Size_List_Model = new DefaultComboBoxModel (sizes); } // Text: /** Sets the text to be displayed in the current font.

    @param text The String of text to be displayed. @return This Font_Selector. */ public Font_Selector Text ( String text ) { Sample_Text.setText (text); return this; } /** Gets the current sample text. The default text is:

    	ABCDEFGHIJKLMNOPQRSTUVWXYZ
    	abcdefghijklmnopqrstuvwxyz
    	0123456789 ~!@#$%^&*()_+`-=[]\\{}|;':\"<>?,./
    

    @return The String of sample text. */ public String Text () {return Sample_Text.getText ();} // Select: /** Initiates a user interactive font selection.

    This dialog is {@link #setVisible(boolean) setVisible}. When the user completes the selection or closes the dialog, the current font is returned. If the "Cancel" button is pressed, then the original font is restored before the selection operation completes.

    @param default_font The font to be the original, default font for the selection. If null, then the previous original font is used. @return The Font that was selected. This will be the original font if no selection was made or the "Cancle" button was pressed. */ public Font Select ( Font default_font ) { if ((DEBUG & DEBUG_SELECT) != 0) System.out.println (">>> Font_Selector.Select: " + default_font); if (default_font == null) // Start with the original font. Font (Original_Font); else // Start with the specified font as the original font. Font (Original_Font = default_font); setVisible (true); if ((DEBUG & DEBUG_SELECT) != 0) System.out.println ("<<< Font_Selector.Select: " + Font_Name () + ", size " + ((Size () == -1) ? "(none)" : String.valueOf (Size ())) + ", style " + ((Style () == -1) ? "(none)" : Style_List_Model.getElementAt (Style ()))); return Current_Font; } /** Initiates a Select operation using the previous original font.

    @see #Select(Font) */ public Font Select () {return Select (null);} /*============================================================================== Test stub */ private static String _title_ = null; private static boolean _get_font_ = true, _get_style_ = true; private static Vector _sizes_ = new Vector (); private static final JLabel _selected_font_ = new JLabel ("Select a font"); private static Font_Selector _font_selector_ = null; private static int _click_count_ = 0; public static void main (String arguments[]) { final JFrame frame = new JFrame ("Font_Selector"); frame.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent e) {System.exit (0);}}); JPanel content = new JPanel (); frame.setContentPane (content); content.setLayout (new BoxLayout (content, BoxLayout.X_AXIS)); if (arguments.length > 0) { _title_ = arguments[0]; if (arguments.length > 1) { _get_font_ = _get_style_ = false; for (int arg = 1; arg < arguments.length; arg++) { if (arguments[arg].equalsIgnoreCase ("font")) _get_font_ = true; else if (arguments[arg].equalsIgnoreCase ("style")) _get_style_ = true; else { try {_sizes_.add (new Integer (arguments[arg]));} catch (NumberFormatException exception) {} } } if (_sizes_.isEmpty ()) _sizes_ = null; } } _font_selector_ = new Font_Selector ( _title_, _get_font_, _get_style_, _sizes_, new Font ("Serif", Font.BOLD, 14)); JButton button = new JButton ("Font..."); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) { if (_click_count_++ == 1) _font_selector_.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) {_apply_font_ ();}}); _font_selector_.Select (); _apply_font_ (); }}); button.setAlignmentX (JComponent.CENTER_ALIGNMENT); content.add (button); content.add (_selected_font_); content.setPreferredSize (new Dimension (500, 50)); frame.pack (); frame.setVisible (true); } private static void _apply_font_ () { Font font = _font_selector_.Font (); _selected_font_.setText (font.toString ()); _selected_font_.setFont (font); } } // End of Font_Selector class. pirl-2.3.8/PIRL/Viewers/Memory_Chart.java0000644000175000017500000004361511742735302020007 0ustar mathieumathieu/* Memory_Chart PIRL CVS ID: Memory_Chart.java,v 1.5 2012/04/16 06:22:58 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JComponent; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.Font; import java.awt.Dimension; import java.awt.geom.Rectangle2D; import java.awt.Graphics; /** A Memory_Chart provides a self-updating graphical display of the Java runtime environment (JRE) memory usage.

    The JRE reports three categories of memory: the total amount of {@link #Available_Memory() available memory} in the JRE heap space, the amount of {@link #Allocated_Memory allocated memory} used by objects, and the amount of {@link #Free_Memory() free memory} that is no longer in use by objects which can be garbage collected.

    Memory use over time is stored in a Memory_History that samples the JRE memory categories at a user specified rate and stores the values in history arrays up to some maximum number of values. When the maximum number of samples has been accumulated the next sample causes the oldest value to be dropped before the new value is added. Obtaining a sample also results in the component display to be updated by displaying all current memory history values as a horizontally time-ordered (oldest on the left) chart with one vertical bar per sample. Each sample bar is divided into three sections; from top to bottom: the amount of unallocated memory ({@link #Available_Memory() available memory} minus {@link #Allocated_Memory() allocated memory}) in blue, the amount of {@link #Free_Memory() free memory} in green, and the amount of memory in use (@link #Allocated_Memory() allocated memory} minus {@link #Free_Memory() free memory}) in red. The visual effect is a memory use chart that automatically scrolls from right to left as new samples are acquired.

    Below the memory use bars a {@link #Seconds_Scale(boolean) seconds scale} is optionally displayed with short tick marks every fifth bar and long tick marks every tenth bar. Also, if the sampling rate is more often than every 20 seconds a thick long mark is provided for each minute. The total duration, in seconds, of the history chart is written at the left end of the seconds scale. If annotations are enabled and the sampling rate is not once per second the scale multiplication factor is shown.

    To the right of the chart {@link #Annotation(boolean) annotation} may be optionally displayed. This will list the most recent memory sample using the same values and colors as used in the chart bars.

    Memory sampling may be stopped and restarted. When sampling is restarted the memory history is cleared. The sampling rate may be changed at any time which will also clear the current memory history.

    The component will automatically manage the Memory_History by ensuring that there is always enough memory history for the chart display width.

    N.B.: The Memory_History sampling can not be guaranteed to always occur at the selected rate; other activity on the system may prevent a sampling timer event from occurring at the intended time. Nevertheless, there will still be the correct number of samples generated for the overall amount of elapsed time. To achieve this there may be occasional bursts of catch-up sampling events that will result in memory values that have not been sampled at the expected time; but no sampling events will be dropped. Thus overall the memory chart will be a reasonable reflection of the JRE memory use profile with any particular sample occurring as close to the expected time as the system allows.

    @author Bradford Castalia - idaeim @version 1.5 @see Memory_History */ public class Memory_Chart extends JComponent implements ChangeListener { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Memory_Chart (1.5 2012/04/16 06:22:58)"; /** The initial size of the component. */ public static final Dimension DEFAULT_SIZE = new Dimension (200, 70); /** The text font that will be used to provide chart annotations. */ public static final Font ANNOTATION_FONT = new Font ("Monospaced", Font.PLAIN, 10); //! Should the memory values annotations be displayed? private boolean Annotation = true; private int Annotation_Area_Width = 0, Annotation_Text_Height = 0; //! Working values until the real values can be determined. private static final int DEFAULT_ANNOTATION_AREA_WIDTH = 33, DEFAULT_ANNOTATION_TEXT_HEIGHT = 11; //! Should the seconds scale be displayed? private boolean Seconds_Scale = true; private int Seconds_Scale_Height = 0; //! Working value until the real value can be determined. private static final int DEFAULT_SECONDS_SCALE_HEIGHT = DEFAULT_ANNOTATION_TEXT_HEIGHT; //! The memory usage history model. private Memory_History History = null; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_UI = 1 << 1, DEBUG_MODEL = 1 << 2, DEBUG_GEOMETRY = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Memory_Chart that samples at a specified rate.

    @param sampling_rate The number of seconds between memory samples. A sampling rate of zero constructs an inactive chart. */ public Memory_Chart ( int sampling_rate ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Memory_Chart.Constructor"); UIManager.put ("Memory_ChartUI", "Memory_Chart_UI"); if (sampling_rate < 0) sampling_rate = 0; History = new Memory_History (DEFAULT_SIZE.width - Annotation_Area_Width (), sampling_rate); Dimension dimension = new Dimension (DEFAULT_SIZE); if (Annotation_Text_Height != 0) // Allow for four annotation lines and the seconds scale. dimension.height = Annotation_Text_Height * 5; History.addChangeListener (this); setMinimumSize (dimension); setPreferredSize (dimension); setUI (new Memory_Chart_UI ()); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Memory_Chart.Constructor"); } /** Construct an idle Memory_Chart.

    A {@link #Rate(int) sampling rate} must be set before the chart will become active. */ public Memory_Chart () {this (0);} /*============================================================================== Geometry */ /** Enable or disable the display of memory values annotation.

    When enabled an area on the right side of the memory chart will be used to display, from top to bottom: the total amount of {@link #Available_Memory() available memory} in black, the amount of unallocated memory ({@link #Available_Memory() available memory} minus {@link #Allocated_Memory() allocated memory}) in blue, the amount of {@link #Free_Memory() free memory} ({@link #Allocated_Memory() allocated} but not used by any objects; i.e. subject to garbage collection) in green, and the amount of memory in use (@link #Allocated_Memory() allocated memory} minus {@link #Free_Memory() free memory}) in red. Each value is in the range 0-1023 with a magnitude suffix character - "K" for kilobytes, "M" for megabytes, "G" for gigabytes - as appropriate.

    @param enabled If true (the default) the annotation display will be enabled. If false, annoation will not be displayed. @return This Memory_Chart. @see #Annotation_Area_Width() */ public Memory_Chart Annotation ( boolean enabled ) { if (Annotation != enabled) { boolean annotation = Annotation; Annotation = enabled; firePropertyChange ("Annotation", annotation, Annotation); repaint (); } return this; } /** Check if the annotation display is enabled.

    @return true if the annotation display is enabled; false otherwise. */ public boolean Annotation () {return Annotation;} /** Get the width of the annotation display area.

    The annotation display occupies the right end of the component area. The value will be zero if the {@link #Annotation(boolean) annotation} display is not enabled. If the appropriate size can not be determined from the {@link #ANNOTATION_FONT annotation font} (because a component rendering graphics device has not yet been assigned) the width will be a guesstimate based on a typical display device of approximately 100 pixels per inch.

    @return The width, in pixels, of the a annotation display area, or zero if annotation is disabled. */ public int Annotation_Area_Width () { int value = 0; if (Annotation) { if (Annotation_Area_Width == 0) { Reset_Geometry (); value = (Annotation_Area_Width == 0) ? DEFAULT_ANNOTATION_AREA_WIDTH : Annotation_Area_Width; } else value = Annotation_Area_Width; } return value; } /** Get the height of the annotation text.

    If the appropriate size can not be determined from the {@link #ANNOTATION_FONT annotation font} (because a component rendering graphics device has not yet been assigned) the text height will be a guesstimate based on a typical display device of approximately 100 pixels per inch.

    @return The height, in pixels, of the a annotation text. */ public int Annotation_Text_Height () { if (Annotation_Text_Height == 0) Reset_Geometry (); return (Annotation_Text_Height == 0) ? DEFAULT_ANNOTATION_TEXT_HEIGHT : Annotation_Text_Height; } /** Enable or disable the display of the seconds scale.

    When enabled the seconds scale an area at the bottom of the memory chart will be used to display tick marks to aid in counting the number of memory samples in the chart. Short tick marks will occur every fifth sample measured from the most recent sample on the right end of the chart. Long tick marks will occur every tenth sample. If the sampling rate is more often than every 20 seconds a thick long mark is provided for each minute in the sampling history. The total duration, in seconds, of the history chart is written at the left end of the seconds scale when there is sufficient space. If {@link #Annotation(boolean) annotation} is enabled "secs" will be written to the right of the seconds scale in the annotation area. However, if the sampling rate is not once per second the annotation is the scale multiplication factor preceeded by "x" and followed by " s".

    @param enabled If true (the default) the seconds scale display will be enabled. If false, the seconds scale will not be displayed. @return This Memory_Chart. @see #Seconds_Scale_Height() */ public Memory_Chart Seconds_Scale ( boolean enabled ) { if (Seconds_Scale != enabled) { boolean seconds_scale = Seconds_Scale; Seconds_Scale = enabled; firePropertyChange ("Seconds_Scale", seconds_scale, Seconds_Scale); repaint (); } return this; } /** Check if the seconds scale display is enabled.

    @return true if the seconds scale display is enabled; false otherwise. */ public boolean Seconds_Scale () {return Seconds_Scale;} /** Get the height of the seconds scale display area.

    If the appropriate size can not be determined from the {@link #ANNOTATION_FONT annotation font} (because a component rendering graphics device has not yet been assigned) the height will be a guesstimate based on a typical display device of approximately 100 pixels per inch.

    @return The height, in pixels, of the seonds scale display area. */ public int Seconds_Scale_Height () { int value = 0; if (Seconds_Scale) { if (Seconds_Scale_Height == 0) { Reset_Geometry (); value = (Seconds_Scale_Height == 0) ? DEFAULT_SECONDS_SCALE_HEIGHT : Seconds_Scale_Height; } else value = Seconds_Scale_Height; } return value; } /** Determine the annotation and seconds scale sizes.

    If a graphics display device can be obtained for the component the annotation and seconds scale sizes are determined from the {@link #ANNOTATION_FONT} metrics. The annotation area width is the rendered width of the string "1055M" plus 3; the seconds scale height is the same as the annotation text height. If, however, a graphics display device for the component is not currently known then all values are set to zero. */ private void Reset_Geometry () { if ((DEBUG & DEBUG_GEOMETRY) != 0) System.out.println (">>> Memory_Chart.Reset_Geometry"); Graphics graphics = getGraphics (); if (graphics != null) { Rectangle2D bounds = getFontMetrics (ANNOTATION_FONT) .getStringBounds ("1055M", graphics); Annotation_Area_Width = (int)(bounds.getWidth ()) + 3; Annotation_Text_Height = (int)(bounds.getHeight ()); Seconds_Scale_Height = Annotation_Text_Height; } else { Annotation_Area_Width = Annotation_Text_Height = Seconds_Scale_Height = 0; } if ((DEBUG & DEBUG_GEOMETRY) != 0) System.out.println (" Annotation_Area_Width = " + Annotation_Area_Width + '\n' +" Annotation_Text_Height = " + Annotation_Text_Height + '\n' +" Seconds_Scale_Height = " + Seconds_Scale_Height + '\n' +"<<< Memory_Chart.Reset_Geometry"); } /*============================================================================== Model */ /** Set a new Memory_History model for the component.

    @param model A Memory_History object. */ public void setModel ( Memory_History model ) { Memory_History previous = History; if (model == null) model = new Memory_History (getWidth (), 1); History = model; History.addChangeListener (this); firePropertyChange ("model", previous, model); } /** Get the Memory_History model bound to the component.

    @return A Memory_History object. */ public Memory_History getModel () {return History;} /** Set the memory history sampling rate.

    If the new samping rate is different than the current sampling rate the {@link Memory_History#Rate(int) rate} is changed and a "rate" property change event is fired. The {@link #Previous_Rate() previous rate}, if non-zero, is remembered.

    N.B.: If the rate is less than or equal to zero the chart will be {@link #Stop() stopped}.

    @param rate The memory history sampling rate to be used. @return This Memory_Chart. */ public Memory_Chart Rate ( int rate ) { int previous_rate = History.Rate (); if (previous_rate != rate) { History.Rate (rate); firePropertyChange ("rate", previous_rate, rate); } return this; } /** Stop the memory chart.

    Memory sampling will be stopped. This is the same as setting the {@link #Rate(int) sampling rate} to zero. */ public void Stop () {History.Rate (0);} /** Start the chart memory sampling.

    If the current {@link #Rate() sampling rate} is zero and the {@link #Previous_Rate() previous rate} was non-zero, the memory history is {@link Memory_History#Clear() cleared} and its sampling rate is reset to the previous rate. */ public void Start () { if (History.Rate () == 0 && History.Previous_Rate () != 0) { History.Clear (); History.Rate (History.Previous_Rate ()); } } /** Get the memory sampling rate.

    @return The current memory history sampling rate. */ public int Rate () {return History.Rate ();} /** Get the previous memory sampling rate.

    @return The previous memory history sampling rate. This will be zero only if there was no non-zero previous sampling rate. */ public int Previous_Rate () {return History.Previous_Rate ();} /** The ChangeListener interface method.

    The action is to repaint the component. */ public void stateChanged ( ChangeEvent event ) {repaint ();} /** Get the amount of memory allocated by the Java runtime environment but not currently in use.

    @return The amount, in bytes, of allocated but free memory. @see Memory_History#Available_Memory() */ public static long Free_Memory () {return Memory_History.Free_Memory ();} /** Get the amount of memory currently in use by the Java runtime environment.

    @return The amount, in bytes, of allocated memory. @see Memory_History#Available_Memory() */ public static long Allocated_Memory () {return Memory_History.Allocated_Memory ();} /** Get the total amount of memory available to the Java runtime environment.

    @return The total amount of memory, in bytes, available to the Java runtime environment. @see Memory_History#Available_Memory() */ public static long Available_Memory () {return Memory_History.Available_Memory ();} /*============================================================================== View */ /** Set the graphical user interface (UI) delegate for the component.

    @param UI A Memory_ChartUI object that will paint the component and handle other user interface events. @see JComponent#setUI(ComponentUI) */ public void setUI ( Memory_ChartUI UI ) {super.setUI (UI);} /** Set the user interface delegate to the ComponentUI found by the UIManger for this component.

    This component is invalidated.

    @see #getUIClassID() */ public void updateUI () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> Memory_Chart.updateUI"); setUI ((Memory_ChartUI)UIManager.getUI (this)); invalidate (); if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< Memory_Chart.updateUI"); } /** Get the ComponentUI class identification for this component.

    @return The String "Memory_ChartUI". */ public String getUIClassID () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">-< Memory_Chart.getUIClassID: Memory_ChartUI"); return "Memory_ChartUI"; } } // End of Memory_Chart class. pirl-2.3.8/PIRL/Viewers/Blinker.java0000644000175000017500000002121411742735302016773 0ustar mathieumathieu/* Blinker PIRL CVS ID: Blinker.java,v 1.8 2012/04/16 06:22:58 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.Timer; import java.awt.Component; import java.awt.Color; import java.awt.event.*; /** An ActionListener for blinking a component to make it stand out from the crowd.

    Blinking occurs by setting the background color of the component to a specified color, and then returning to the original color, with a delay interval between background color switching. This on-off cycle is repeated for a specified number of times, or until instructed to stop. An optional delay after the new background color is first switched on until the blinking intervals start may be specified.

    @author Bradford Castalia, UA/PIRL @version 1.8 */ public class Blinker extends Timer implements ActionListener { private static final String ID = "PIRL.Viewers.Blinker (1.8 2012/04/16 06:22:58)"; /** Constant to specify an indefinate number of blinks; i.e. forever until stopped. */ public static final int INDEFINITELY = Integer.MAX_VALUE; /** The default number of blink repeats: INDEFINITELY. */ public static final int REPEATS = INDEFINITELY; /** The default initial delay before blinking starts: 0 milliseconds (immediately after start). */ public static final int INITIAL_DELAY = 0; /** The default blink interval: 500 milliseconds (half a second). */ public static final int INTERVAL = 500; /** The minimum blink interval: 100 milliseconds. */ public static final int MINIMUM_INTERVAL = 100; /** The default background Color to use when the component is blinked "on": yellow. */ public static final Color COLOR = Color.yellow; private Component Blinkee; private Color Colors[] = new Color[2]; // 0: off (component), 1: on (blink) private int Blinks = 0, Max_Blinks; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_TIMER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a component Blinker.

    @param component The Component to be blinked. @param color The Color to set the component background when it is blinked "on". @param repeats The maximum number of blinks. Use the {@link #INDEFINITELY} value to continue blinking until stopped. @param initial_delay The initial delay, after the component is turned on, before starting to blink. A value <= 0 means no initial delay. @param interval The interval between blinks (on to off, or off to on). A {@link #MINIMUM_INTERVAL} is enforced. */ public Blinker ( Component component, Color color, int repeats, int initial_delay, int interval ) { super (((interval < MINIMUM_INTERVAL) ? MINIMUM_INTERVAL : interval), null); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Blinker:\n" +" component - " + component + '\n' +" color - " + color + '\n' +" repeats - " + repeats + '\n' +" initial_delay - " + initial_delay + '\n' +" interval - " + interval); addActionListener (this); if (initial_delay < 0) initial_delay = 0; setInitialDelay (initial_delay); setRepeats (true); setRepeats (repeats); Blinkee = component; if (component != null) { Colors[0] = component.getBackground (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" background - " + Colors[0]); } Colors[1] = color; if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Blinker"); } /** Constructs a Blinker for the component using all of the default Blinker parameters.

    @param component The Component to be blinked. */ public Blinker ( Component component ) {this (component, COLOR, REPEATS, INITIAL_DELAY, INTERVAL);} /** Constructs a disabled Blinker with default Blinker parameters. */ public Blinker () {this (null, COLOR, REPEATS, INITIAL_DELAY, INTERVAL);} /*============================================================================== Timer methods overrides */ /** Turns the component "on" (sets the new background color) and starts the blink timer. The blink count picks up from its previous value. Use restart to reset the blink count. */ public void start () { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Blinker.start"); if (Blinkee != null && Blinks < Max_Blinks) { // Turn the component "on". if ((Blinks % 2) == 0) Blinks++; if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" " + Blinks + " blinks"); Blinkee.setBackground (Colors[1]); super.start (); } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Blinker.start: isRunning " + isRunning ()); return; } /** Stops the blink timer and turns the component "off" (sets the original background color). The blink count is not reset. */ public void stop () { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Blinker.stop"); super.stop (); // Turn the component "off". if ((Blinks % 2) != 0) Blinks--; if (Blinkee != null) { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Restoring background"); Blinkee.setBackground (Colors[0]); } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Blinker.stop"); } /** Restarts the Blinker as if no blinks had occured. */ public void restart () { stop (); Blinks = 0; start (); } /** Sets the maximum number of blinks.

    @param repeats The maximum number of times to blink the component. Use the {@link #INDEFINITELY} value to continue blinking until stopped. @return The previous value. */ public int setRepeats ( int repeats ) { int max = Max_Blinks; if (repeats < 0) repeats = 0; Max_Blinks = repeats; return max; } /** Gets the maximum number of blinks.

    @return The maximum number of times to blink the component. For indefinate blinking this will be the {@link #INDEFINITELY} value. */ public int getRepeats () {return Max_Blinks;} /*============================================================================== Extension methods */ /** Sets the background color to use when blinking the component.

    @param color The background color when the component is "on". @return The previous value. */ public Color setColor ( Color color ) { Color previous_color = Colors[1]; Colors[1] = color; return previous_color; } /** Gets the background color that will be used when blinking the component.

    @return The background blink Color. */ public Color getColor () {return Colors[1];} /** Sets the Component to be blinked.

    Any currently blinking component is stopped and the blink count is reset to zero. The new component will not yet be blinking.

    @param component The Component to be blinked. @return This blinker. */ public Blinker setComponent (Component component) { stop (); Blinks = 0; if ((Blinkee = component) != null) Colors[0] = component.getBackground (); return this; } /** Gets the Component to be blinked.

    @return The Component to be blinked. */ public Component getComponent () {return Blinkee;} /** Performs the blink event at the Timer interval. Switches the component from its "on" background color to its "off" background color, or vice versa. */ public void actionPerformed (ActionEvent event) { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (">>> Blinker.actionPerformed"); if (Blinks >= Max_Blinks || Blinkee == null) { if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Stopping after " + Blinks + " blinks"); Blinks = 0; stop (); } else if (Blinkee != null) { Blinkee.setBackground (Colors[++Blinks % 2]); if ((DEBUG & DEBUG_TIMER) != 0) System.out.println (" Background set to " + Colors[Blinks % 2]); } if ((DEBUG & DEBUG_TIMER) != 0) System.out.println ("<<< Blinker.actionPerformed"); } } // class Blinker. pirl-2.3.8/PIRL/Viewers/Parameter_Pane.java0000644000175000017500000006542211742735303020302 0ustar mathieumathieu/* Parameter_Pane PIRL CVS ID: Parameter_Pane.java,v 1.21 2012/04/16 06:22:59 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.PVL.*; import PIRL.TreeTable.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.table.*; import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.File; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; import java.util.Iterator; // Used the by the testing stub: import PIRL.Utilities.Streams; import javax.swing.JFrame; import java.io.InputStream; /** A Parameter_Pane provides a graphical view of a Parameter.

    The display uses a Parameter_Model to wrap the Parameter data and a JTreeTable controller to represent the Parameters and its Values in two columns. Any Parameter that is an aggregate of other Parameters may be expanded to display its contents. The name of each Parameter is listed in the first column of the table. In the second column the Parameter's Value is shown as the String provided by the Value's toString method. Those Values that are array types (SET or SEQUENCE) may be shown in various display {@link #Array_Viewing_Mode(int) modes}.

    @see Parameter @see Value @see JTreeTable @author Bradford Castalia, UA/PIRL @version 1.21 */ public class Parameter_Pane extends JPanel { private static final String ID = "PIRL.Viewers.Parameter_Pane (1.21 2012/04/16 06:22:59)"; /** Array Value elements are viewed as comma separated values (CSV) text. */ public static final int CSV_ARRAY_VIEWING = 0; /** Array Value elements are viewed in a scrollable subtree cell pane. */ public static final int SUBTREE_ARRAY_VIEWING = 1; /** Array Value elements are viewed in a separate window. */ public static final int WINDOW_ARRAY_VIEWING = 2; /** Array Value elements are viewed in multiple separate windows. */ public static final int MULTIPLE_WINDOW_ARRAY_VIEWING = 3; /** The default Array Value viewing mode. */ public static int Default_Array_Viewing_Mode = WINDOW_ARRAY_VIEWING; /** Current Array Value viewing mode. */ protected int Array_Viewing_Mode = Default_Array_Viewing_Mode; /** Default tree line style. */ public static String Default_Line_Style = "None"; private JScrollPane Parameters_Pane; /** The JTreeTable used to view the Parameter. */ protected JTreeTable The_TreeTable; /** The Parameter being viewed. */ private Parameter The_Parameter; // A Value to Value_View table for current Value Array displays. private Hashtable Value_Views = new Hashtable (); // Indicates that a new Value_View has just been created. private boolean New_Value = false; // A flag to wave over a Value_View. private static final Blinker Value_View_Blinker = new Blinker ( null, Color.YELLOW, 8, // Repeats. 500, // Initial delay (ms). 150 // Blink interval (ms). ); // The last path expanded in the values column. private TreePath Last_Value_Path_Expanded = null; // Parameter comments pane. private JTextArea Comments_Text = null; // Array Value viewing control menu. private JPopupMenu Popup_Menu; private JMenu Array_Values_Menu; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_UI = 1 << 1, DEBUG_EDITOR = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Parameter_Pane from the specified Parameter.

    @param parameter The Parameter to be displayed. */ public Parameter_Pane ( Parameter parameter ) { setLayout (new BorderLayout ()); // Popup menu. Popup_Menu = new JPopupMenu ("View"); Popup_Menu.add (Array_Values_Menu = Popup_Menu ()); // Parameters table. Parameters_Pane = new JScrollPane (); Parameter (parameter); // Comments pane. JSplitPane split_pane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, true, Parameters_Pane, Comments_Pane ()); split_pane.setResizeWeight (1.0); split_pane.setDividerLocation (-1); add (split_pane); } /*============================================================================== Accessors */ /** Sets the Parameter to be viewed.

    N.B.: If the Name of the Parameter is the {@link Parser#CONTAINER_NAME} then the root node will not be displayed.

    @param parameter The Parameter to View. @return This Parameter_Pane. */ public Parameter_Pane Parameter ( Parameter parameter ) { if (parameter == null) parameter = new Parameter (); // The JTreeTable for the Parameter. The_TreeTable = Tree_Table (new Parameter_Model (The_Parameter = parameter)); if (The_Parameter.Name ().equals (Parser.CONTAINER_NAME)) { // Don't show The Container group node. The_TreeTable.getTree ().setRootVisible (false); The_TreeTable.getTree ().setShowsRootHandles (true); } // The JTree line style. Line_Style (Default_Line_Style); // Take down our Value_Views. Take_Down_Value_Views (Value_Views); Parameters_Pane.setViewportView (The_TreeTable); return this; } /** Gets the Parameter being viewed in the display.

    @return The Parameter viewed in this Parameter_View. */ public Parameter Parameter () {return The_Parameter;} /** Gets the "JTree.lineStyle" property currently being used.

    @return The "JTree.lineStyle" property String in effect. */ public String Line_Style () {return (String)The_TreeTable.getTree ().getClientProperty ("JTree.lineStyle");} /** Sets the "JTree.lineStyle" property to the specified style.

    @param style The String describing the style to use. @return This Parameter_Pane. @see javax.swing.JComponent#putClientProperty(Object, Object) */ public Parameter_Pane Line_Style ( String style ) { The_TreeTable.getTree ().putClientProperty ("JTree.lineStyle", style); return this; } /** Gets the JTreeTable being used to view the Parameter in the display.

    @return The JTreeTable viewed in this Parameter_View. */ public JTreeTable TreeTable () {return The_TreeTable;} /** Gets the Array Value viewing mode.

    @return The Value Array viewing mode. This will be one of {@link #CSV_ARRAY_VIEWING}, {@link #SUBTREE_ARRAY_VIEWING}, {@link #WINDOW_ARRAY_VIEWING}, or {@link #MULTIPLE_WINDOW_ARRAY_VIEWING}. */ public int Array_Viewing_Mode () {return Array_Viewing_Mode;} /** Sets the Array Value viewing mode.

    The current view will be redisplayed as appropriate.

    @param mode The Value Array viewing mode. This should be one of {@link #CSV_ARRAY_VIEWING}, {@link #SUBTREE_ARRAY_VIEWING}, or {@link #WINDOW_ARRAY_VIEWING}. A value less than 0 will be set to 0; a value greater than {@link #MULTIPLE_WINDOW_ARRAY_VIEWING} will be set to {@link #MULTIPLE_WINDOW_ARRAY_VIEWING}. @return This Parameter_Pane. */ public Parameter_Pane Array_Viewing_Mode ( int mode ) { if (mode < 0) mode = 0; else if (mode > MULTIPLE_WINDOW_ARRAY_VIEWING) mode = MULTIPLE_WINDOW_ARRAY_VIEWING; if (Array_Viewing_Mode != mode) { Array_Viewing_Mode = mode; Refresh (); } return this; } /** Sets the path to the last value expanded.

    NOT FOR PUB USE!

    N.B.: This method is an implementation artifact that is only for use by a Value_Pane created by this Parameter_Pane.

    @param last_path_expanded The TreePath for the last expanded Array Value row. */ public void Last_Value_Path_Expanded ( TreePath last_path_expanded ) {Last_Value_Path_Expanded = last_path_expanded;} /*============================================================================== Display Elements */ /** Refresh the view.

    Any change to the {@link #Array_Viewing_Mode(int) array viewing mode} is applied. */ public void Refresh () { JScrollBar scroll_bar = Parameters_Pane.getVerticalScrollBar (); int total_rows = The_TreeTable.getTree ().getRowCount (), select_row = The_TreeTable.getTree () .getRowForPath (The_TreeTable.getTree ().getSelectionPath ()); boolean[] expanded_rows = new boolean [total_rows]; for (int row = 0; row < total_rows; row++) expanded_rows[row] = The_TreeTable.getTree ().isExpanded (row); Take_Down_Value_Views (Value_Views); The_TreeTable.removeEditor (); if (Array_Viewing_Mode == SUBTREE_ARRAY_VIEWING) The_TreeTable.setDefaultEditor (Value.class, new Value_Cell_Editor ()); else if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING || Array_Viewing_Mode == MULTIPLE_WINDOW_ARRAY_VIEWING) { The_TreeTable.getSelectionModel () .addListSelectionListener (new List_Selector ()); Parameter_Selected (); } // Update the Comments. Update_Comments (); Parameters_Pane.setViewportView (The_TreeTable); for (int row = 0; row < total_rows; row++) if (expanded_rows[row]) The_TreeTable.getTree ().expandRow (row); The_TreeTable.getTree ().addSelectionRow (select_row); The_TreeTable.editCellAt (select_row, 1); Parameters_Pane.setVerticalScrollBar (scroll_bar); } /** Creates the JTreeTable that will display the Parameter's name and Value.

    The JTreeTable is constructed using the specified Parameter_Model.

    A ListSelectionListener is provided for the JTreeTable that will create a Value_View when a row in the table is selected that contains an Array Value.

    The Value_View displays that are being viewed are maintained in the Value_Views Hashtable. When the Array_Viewing_Mode is WINDOW_ARRAY_VIEWING any Value_View for a non-selected Value is disposed of. If the Array_Viewing_Mode is MULTIPLE_WINDOW_ARRAY_VIEWING and a previously selected Value has a Value_Views entry, then that display is flashed with a Blinker to bring attention to the reselected Value display.

    @param model The Parameter_Model to use in constructing the JTreeTable. @return The JTreeTable that was created. */ protected JTreeTable Tree_Table ( Parameter_Model model ) { JTreeTable tree_table = new JTreeTable (model); // Supress the icon for leaf nodes. TreeCellRenderer renderer = tree_table.getTree ().getCellRenderer (); if (renderer instanceof DefaultTreeCellRenderer) ((DefaultTreeCellRenderer)renderer).setLeafIcon (null); // Set the Array Value viewing mode. if (Array_Viewing_Mode == SUBTREE_ARRAY_VIEWING) tree_table.setDefaultEditor (Value.class, new Value_Cell_Editor ()); else if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING || Array_Viewing_Mode == MULTIPLE_WINDOW_ARRAY_VIEWING) { tree_table .getSelectionModel () .addListSelectionListener (new List_Selector()); } // No cell editor for CSV mode. if (Popup_Menu != null) tree_table.addMouseListener (new Popup_Listener ()); return tree_table; } /** Handle a selected Parameter.

    The Value_View displays that are being viewed are maintained in the Value_Views Hashtable. When the Array_Viewing_Mode is WINDOW_ARRAY_VIEWING any Value_View for a non-selected Value is disposed of. If the Array_Viewing_Mode is MULTIPLE_WINDOW_ARRAY_VIEWING and a previously selected Value has a Value_Views entry, then that display is flashed with a Blinker to bring attention to the reselected Value display. */ private void Parameter_Selected () { // Determine the Parameters that were selected. int[] selected_rows = The_TreeTable.getSelectedRows (); Hashtable old_Value_Views = Value_Views; if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING) // Re-assemble the list of Value displays. Value_Views = new Hashtable (); Point view_location = getLocationOnScreen (); view_location.x += 4 * (getSize ().width / 5); for (int selection = 0; selection < selected_rows.length; selection++) { Parameter parameter = (Parameter)The_TreeTable.getTreeNode (selected_rows[selection]); Value value; try {value = parameter.Value ();} catch (PVL_Exception exception) {continue;} if (parameter.Is_Assignment () && value.Is_Array ()) { // Value Array selected. Value_View view; if (! old_Value_Views.containsKey (value)) { // New view. try { view = new Value_View (parameter.Path_Name (), value, this); if (! view.Value_Pane ().Line_Style ().equals (Line_Style ())) view.Value_Pane ().Line_Style (Line_Style ()); view.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent event) { Window window = event.getWindow (); Enumeration views = Value_Views.keys (); while (views.hasMoreElements ()) { Value view_value = (Value)views.nextElement (); if (Value_Views.get (view_value) == window) Value_Views.remove (view_value); } window.dispose (); }}); Rectangle cell_bounds = The_TreeTable.getCellRect (selected_rows[selection], 0, false); view_location.y = The_TreeTable.getLocationOnScreen ().y + cell_bounds.y + (cell_bounds.height / 2); view.setLocation (view_location); view.setVisible (true); view.Tree ().expandRow (0); } catch (PVL_Exception exception) { Dialog_Box.Error ("Unable to create the Value_View.\n\n" + ID + '\n' + exception.Message ()); continue; } Value_Views.put (value, view); New_Value = true; } else { // Previously selected view. if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING) // Copy over the old Value_View. Value_Views.put (value, old_Value_Views.remove (value)); else if (New_Value) New_Value = false; else { // Wave a flag over the view. Value_View_Blinker.setComponent (((Value_View)Value_Views.get (value)).Tree ()); Value_View_Blinker.restart (); } } } } if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING) // Take down any deselected views. Take_Down_Value_Views (old_Value_Views); } /** Any windows displaying Array Values are taken down. */ public void Take_Down_Value_Views () {Take_Down_Value_Views (Value_Views);} private void Take_Down_Value_Views ( Hashtable value_views ) { Enumeration views = value_views.keys (); while (views.hasMoreElements ()) ((Window)value_views.get (views.nextElement ())).dispose (); value_views.clear (); } private JScrollPane Comments_Pane () { Comments_Text = new JTextArea (); Comments_Text.setTabSize (4); JScrollPane scroll_pane = new JScrollPane (Comments_Text); scroll_pane.setViewportBorder (BorderFactory.createTitledBorder ("Comments")); scroll_pane.setMinimumSize (new Dimension (0, 100)); return scroll_pane; } private void Update_Comments () { String comments = null; // Determine the Parameters that were selected. int[] selected = The_TreeTable.getSelectedRows (); if (selected.length == 1) comments = ((Parameter)The_TreeTable.getTreeNode (selected[0])).Comments (); Comments_Text.setText (comments); } /*------------------------------------------------------------------------------ Popup Menu */ /** Get the menu that controls how Array Values are viewed.

    @return The Array Values JMenu. */ public JMenu Array_Values_Menu () {return Array_Values_Menu;} private JMenu Popup_Menu () { JMenu menu = new JMenu ("Array Values"), submenu; ButtonGroup button_group = new ButtonGroup (); JRadioButtonMenuItem radio_button; radio_button = new JRadioButtonMenuItem ("CSV"); radio_button.setMnemonic ('C'); radio_button.setAccelerator (KeyStroke.getKeyStroke ('C', Event.CTRL_MASK)); radio_button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Array_Viewing_Mode (CSV_ARRAY_VIEWING);}}); radio_button.setSelected (Default_Array_Viewing_Mode == CSV_ARRAY_VIEWING); button_group.add (radio_button); menu.add (radio_button); radio_button = new JRadioButtonMenuItem ("Subtree"); radio_button.setMnemonic ('T'); radio_button.setAccelerator (KeyStroke.getKeyStroke ('T', Event.CTRL_MASK)); radio_button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Array_Viewing_Mode (SUBTREE_ARRAY_VIEWING);}}); radio_button.setSelected (Default_Array_Viewing_Mode == SUBTREE_ARRAY_VIEWING); button_group.add (radio_button); menu.add (radio_button); submenu = new JMenu ("Windows"); radio_button = new JRadioButtonMenuItem ("Single"); radio_button.setMnemonic ('L'); radio_button.setAccelerator (KeyStroke.getKeyStroke ('L', Event.CTRL_MASK)); radio_button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Array_Viewing_Mode (WINDOW_ARRAY_VIEWING);}}); radio_button.setSelected (Default_Array_Viewing_Mode == WINDOW_ARRAY_VIEWING); button_group.add (radio_button); submenu.add (radio_button); radio_button = new JRadioButtonMenuItem ("Multiple"); radio_button.setMnemonic ('M'); radio_button.setAccelerator (KeyStroke.getKeyStroke ('M', Event.CTRL_MASK)); radio_button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Array_Viewing_Mode (MULTIPLE_WINDOW_ARRAY_VIEWING);}}); radio_button.setSelected (Default_Array_Viewing_Mode == MULTIPLE_WINDOW_ARRAY_VIEWING); button_group.add (radio_button); submenu.add (radio_button); menu.add (submenu); return menu; } /*.............................................................................. Popup Menu Listener */ class Popup_Listener extends MouseAdapter { public void mousePressed (MouseEvent event) {maybeShowPopup (event);} public void mouseReleased (MouseEvent event) {maybeShowPopup (event);} private void maybeShowPopup ( MouseEvent event ) { if (event.isPopupTrigger ()) Popup_Menu.show (event.getComponent (), event.getX (), event.getY ()); } } /*=***************************************************************************** List_Selector */ /** A List_Selector to handle the selection of values on the Parameter_Pane.

    Though it will listen for ListSelectionEvents in any viewing mode, it only takes action if one of the Value_View viewing modes is in effect. */ private class List_Selector implements ListSelectionListener { public void valueChanged ( ListSelectionEvent event ) { if (! event.getValueIsAdjusting ()) { if (Array_Viewing_Mode == WINDOW_ARRAY_VIEWING || Array_Viewing_Mode == MULTIPLE_WINDOW_ARRAY_VIEWING) Parameter_Selected (); // Update the Comments. Update_Comments (); } } } // End class List_Selector /*=***************************************************************************** Parameter_Model */ /** Names of the columns in the JTreeTable display. The first column is represented by the Parameter; this must be a form of TreeTableModel. The second column is represented by the Value.

    @see Value */ private static final String[] COLUMN_NAMES = { "Name", "Value" }; /** Types of the columns. The entries correspond to the COLUMN_NAMES array. */ private static final Class[] COLUMN_CLASSES = { TreeTableModel.class, Value.class }; private static final int TREE_TABLE_COLUMN = 0, VALUE_COLUMN = 1; /** A Parameter_Model provides the data model of a Parameter for use with a JTreeTable controller.

    @see Parameter @see Value @see Parameter_Pane @author Bradford Castalia, UA/PIRL @version 1.21 */ class Parameter_Model extends AbstractTreeTableModel { /** Creates a Parameter_Model rooted at the Parameter.

    @param parameter The Parameter to be modeled. */ public Parameter_Model ( Parameter parameter ) {super (parameter);} /*============================================================================== The TreeModel interface */ /** Gets the number of children of a Parameter's node.

    @param node A node of the Parameter being modeled. @return The number of Parameters in the node's aggregate list. @see Parameter#getChildCount() */ public int getChildCount ( Object node ) {return ((Parameter)node).getChildCount ();} /** Gets the child of a Parameter's node at the specified index of the aggregate list.

    @param node A node of the Parameter being modeled. @param index The index into the Parameter node's aggregate list where the child will be found. @return The Parameter Object from the aggregate list. @see Parameter#getChildAt(int) */ public Object getChild ( Object node, int index ) {return ((Parameter)node).getChildAt (index);} /** Tests if a node is a leaf.

    @param node A node of the Parameter being modeled. @see Parameter#isLeaf() */ public boolean isLeaf ( Object node ) {return ((Parameter)node).isLeaf ();} /*============================================================================== The TreeTableNode interface. */ /** Gets the number of columns in the table.

    @return The length of the COLUMN_NAMES array. */ public int getColumnCount () {return COLUMN_NAMES.length;} /** Gets the name for a particular column.

    @param column The column number for which to get a name. @return The corresponding entry in the COLUMN_NAMES array. */ public String getColumnName (int column) {return COLUMN_NAMES[column];} /** Gets the class for a particular column.

    @param column The column number for which to get a class type. @return The corresponding entry in the COLUMN_CLASSES array. */ public Class getColumnClass (int column) {return COLUMN_CLASSES[column];} /** Tests if a table column for a Parameter is editable.

    @param node A node of the Parameter being modeled. @param column A table column number. @return true, it the cell is editable; false otherwise. */ public boolean isCellEditable ( Object node, int column ) { Parameter parameter = (Parameter)node; switch (column) { case 0: return parameter.Is_Aggregate (); case 1: try { return parameter.Value().Is_Array (); } catch (PVL_Exception exception) {return false;} } return false; } /** Gets the value of a particular column for a specified Parameter node representing a table record.

    The first column value is the Parameter's name String.

    The second column value is the Parameter's Value if it has one. If the Parameter is an Aggregate, however, the value is its the name of the Parameter's specific classification. For all other Assignment Parameters a new Value with empty String Data is provided.

    @param node A node of the Parameter being modeled. @param column The column number for which to get a value. @return An Object representing the value of the column. */ public Object getValueAt ( Object node, int column ) { Parameter parameter = (Parameter)node; switch (column) { case 0: return parameter.Name (); case 1: if (parameter.Is_Aggregate ()) return parameter.Classification_Name (); try { Value value = (Value)parameter.Value (); if (value == null) return "[no value]"; if (value.Is_Array () && Array_Viewing_Mode == CSV_ARRAY_VIEWING) return value.Description (); return value; } catch (PVL_Exception exception) {return "";} } return null; } } // End of class Parameter_Model. /*=***************************************************************************** Value_Cell_Editor */ class Value_Cell_Editor extends javax.swing.AbstractCellEditor implements TableCellEditor { private Value_Pane Editor = null; private JTable Table; private int Row, Row_Height; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_EDITOR = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; Value_Cell_Editor () { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println (">-< Parameter_Pane.Value_Cell_Editor"); } // CellEditor (via AbstractCellEditor) interface implementation. public Object getCellEditorValue () { if ((DEBUG & DEBUG_EDITOR) != 0) { System.out.print (">-< Parameter_Pane.Value_Cell_Editor.getCellEditorValue: "); try {Editor.Value ().Write ();} catch (Exception e) {} } if (Editor.Value ().Is_Array ()) Table.setRowHeight (Row, Row_Height); return Editor.Value (); } // TableCellEditor interface implementation. public Component getTableCellEditorComponent ( JTable table, Object value, boolean is_selected, int row, int column ) { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println (">>> Parameter_Pane.Value_Cell_Editor.getTableCellEditorComponent:\n" + " " + row + ',' + column + ": selected = " + is_selected); Editor = null; if (Array_Viewing_Mode != SUBTREE_ARRAY_VIEWING) Table.setRowHeight (Row, Row_Height); else if (value instanceof String) { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println (" String value - " + value); cancelCellEditing (); Editor = null; } else { Value a_value = (Value)value; if ((DEBUG & DEBUG_EDITOR) != 0) { System.out.println (" Value value -"); try {a_value.Write ();} catch (Exception e) {} } Editor = new Value_Pane (a_value, Parameter_Pane.this); Editor.Tree ().setEditable (true); Table = table; Row = row; Row_Height = Table.getRowHeight (); if (a_value.Is_Array ()) { Editor.setPreferredSize (new Dimension (100, 5 * table.getRowHeight ())); Editor.Tree ().expandPath (Last_Value_Path_Expanded); Table.setRowHeight (Row, 5 * Row_Height); if (! Editor.Tree ().isExpanded (0)) /* If the top level has not been expanded, do so. This will occur if the Last_Value_Path_Expanded is null. */ Editor.Tree ().expandRow (0); } } if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println ("<<< Parameter_Pane.Value_Cell_Editor.getTableCellEditorComponent"); return Editor; } } // End of class Value_Cell_Editor. /*============================================================================== Test stub */ public static void main (String[] arguments) { System.out.println (ID); if (arguments.length == 0) { System.err.println ("Usage: java Parameter_Pane "); System.exit (1); } if (System.getProperty ("os.name").equals ("Mac OS X")) { try {UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ());} catch (Exception exception) {/* Just leave the existing LAF. */} } Parameter_Pane parameter_pane = null; try { InputStream source_stream; if ((source_stream = Streams.Get_Stream (arguments[0])) != null) parameter_pane = new Parameter_Pane (new Parameter (new Parser (source_stream))); else { System.err.println ("Unable to access source: " + arguments[0]); System.exit (1); } } catch (Exception exception) { System.err.println (exception.getMessage ()); System.exit (2); } final JFrame frame = new JFrame ("Parameter_Pane"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); frame.setContentPane (parameter_pane); frame.pack (); frame.setVisible (true); } } // End of class Parameter_Pane. pirl-2.3.8/PIRL/Viewers/Parameter_View.java0000644000175000017500000004245111742735303020326 0ustar mathieumathieu/* Parameter_View PIRL CVS ID: Parameter_View.java,v 1.25 2012/04/16 06:22:59 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.PVL.*; import PIRL.TreeTable.*; import PIRL.Utilities.Streams; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.table.*; import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.InputStream; import java.io.File; import java.io.FileOutputStream; import java.io.BufferedOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; /** A Parameter_View provides a graphical view of a Parameter.

    The view contains a Parameter_Pane to display the Parameter data.

    @see Parameter_Pane @see Parameter @author Bradford Castalia, UA/PIRL @version 1.25 */ public class Parameter_View extends JFrame { private static final String ID = "PIRL.Viewers.Parameter_View (1.25 2012/04/16 06:22:59)"; /** Started from main? */ private static boolean _Application_ = false; /** The initial screen location. */ private static final Point DEFAULT_INITIAL_LOCATION = new Point (175, 75); /** Locate a Parameter_View for an opened file. */ private View_Locator Parameter_View_Locator = new View_Locator (); /** The total number of Parameter_View displays being viewed. When this drops to zero the application will exit. */ private static int _Total_Displays_ = 0; /** Workaround hack for OS X TreeTable folder open handle bug. */ private static boolean Use_LAF_Hack = System.getProperty ("os.name").equals ("Mac OS X"); /** For file selection. */ private JFileChooser File_Chooser = new JFileChooser (System.getProperty ("user.dir")); /** For URL selection. */ private URL_Dialog URL_Chooser = new URL_Dialog ("Open URL", null, this, true); /** The display pane for the paramater. */ private Parameter_Pane The_Parameter_Pane; /*============================================================================== Constructors */ /** Creates a Parameter_View from the specified Parameter.

    @param parameter The Parameter to be displayed. */ public Parameter_View ( String title, Parameter parameter ) { if (title == null) { title = parameter.Name (); if (title == null || title.length () == 0) title = "Parameter_View"; } setTitle (title); if (Use_LAF_Hack ()) { try { UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ()); } catch (Exception exception) {/* Just leave the existing L&F. */} } // Assemble the Parameter table pane. The_Parameter_Pane = new Parameter_Pane (parameter); // Assemble the menus. JMenuBar menu_bar = Menu_Bar (); if (menu_bar != null) setJMenuBar (menu_bar); getContentPane ().add (The_Parameter_Pane); // File chooser modes. File_Chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); File_Chooser.setFileHidingEnabled (false); // On window close... addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent event) { if (--_Total_Displays_ == 0 && _Application_) System.exit (0); }}); pack (); _Total_Displays_++; } /** Creates a Parameter_View from the specified Parameter and gives it the Parameter's name as the title.

    @param parameter The Parameter to be displayed. */ public Parameter_View ( Parameter parameter ) {this (null, parameter);} /*============================================================================== Accessors */ /** Gets the Parameter_Pane used by this Parameter_View.

    @return The Parameter_Pane used by this Parameter_View. */ public Parameter_Pane Parameter_Pane () {return The_Parameter_Pane;} /** Gets the Parameter being viewed by this Parameter_View.

    @return The Parameter viewed by this Parameter_View. */ public Parameter Parameter () {return The_Parameter_Pane.Parameter ();} /** Sets the Parameter to be viewed by this Parameter_View.

    @param parameter The Parameter to be viewed by this Parameter_View. @see Parameter_Pane#Parameter(Parameter) */ public void Parameter ( Parameter parameter ) {The_Parameter_Pane.Parameter (parameter);} /** Gets the JTreeTable being used to represent the Parameters in the display.

    @return The JTreeTable representing this Parameter_View. */ public JTreeTable TreeTable () {return The_Parameter_Pane.TreeTable ();} /** Gets the current working directory for finding parameter files. */ public String CWD () {return File_Chooser.getCurrentDirectory ().getAbsolutePath ();} /** Sets the current working directory for finding parameter files.

    @param pathname A host filesystem pathname. If null, the "user.dir" System property will be used. @return This Parameter_View. @throws FileNotFoundException If neither the file nor the file's parent refers to an existing directory. @see #CWD(File) */ public Parameter_View CWD ( String pathname ) throws FileNotFoundException { if (pathname == null) pathname = System.getProperty ("user.dir"); return CWD (new File (pathname)); } /** Sets the current working directory for finding parameter files.

    @param file A File to which to set the CWD. If null, the "user.dir" System property will be used. If the file does not refer to an existing directory, the file's parent will be used. @return This Parameter_View. @throws FileNotFoundException If neither the file nor the file's parent refers to an existing directory. */ public Parameter_View CWD ( File file ) throws FileNotFoundException { if (file == null) file = new File (System.getProperty ("user.dir")); if (! file.isAbsolute ()) file = file.getAbsoluteFile (); if (! file.isDirectory ()) // Can only set directories. file = file.getParentFile (); if (file.exists ()) File_Chooser.setCurrentDirectory (file); else throw new FileNotFoundException (ID + '\n' +"Can't set working directory to " + file.getAbsolutePath () + '\n' +"No such directory."); return this; } /** Tests if the Java (cross platform) Look and Feel is to be used.

    As of Java 1.4 the native Look and Feel for Apple OS X does not function properly with the JTreeTable. The Java Look and Feel does not have this problem. Thus this hack to force its use.

    @return true if the Java Look and Feel hack will be used; false otherwise. */ public static boolean Use_LAF_Hack () {return Use_LAF_Hack;} /** Enables or disables the Java (cross platform) Look and Feel hack.

    As of Java 1.4 the native Look and Feel for Apple Mac OS X does not function properly with the JTreeTable used to display Parameters and their Values. The Java Look and Feel does not have this problem. Thus this hack to force its use. If the hack is disabled the hack will not be used.

    This hack is enabled by default if the "os.name" system property is "Mac OS X"; otherwise it is disabled.

    @param enable true if the Look and Feel hack is to be used; false otherwise. */ public static void Use_LAF_Hack ( boolean enable ) {Use_LAF_Hack = enable;} /*============================================================================== Display Elements */ /** Sets the window title to the filename portion of the name.

    The source may be a file pathname or URL reference. The filename portion of either will be used as the window title.

    @param source The source reference from which to obtain the filename. If null the tile will be blank. @return This Parameter_View. */ public Parameter_View Title ( String source ) { if (source != null && source.length () != 0) { try { URL url = new URL (source); source = url.getPath (); } catch (MalformedURLException except) {} source = new File (source).getName (); } else source = ""; setTitle (source); return this; } /** Sets the window title to the filename portion of the URL.

    @param url The URL from which to obtain the filename. If null the tile will be blank. @return This Parameter_View. */ public Parameter_View Title ( URL url ) { String name = ""; if (url != null) name = new File (url.getPath ()).getName (); setTitle (name); return this; } /** Creates a menu bar for the viewer.

    File
    New
    A JFileChooser is used to select a another file for display of its PVL parameters with a new Parameter_View. The JFileChooser directory is initially the user.dir System property, but it is reset after each file is opened to the directory that contained that file.
    Open
    A JFileChooser is used to select a another file for display of its PVL parameters in the current Parameter_View.
    Close
    Will cause the current window to close. If being run from main and this is the last window the application will exit.
    Exit
    Will cause the application to exit.
    @return The viewer's JMenuBar. */ protected JMenuBar Menu_Bar () { JMenuBar menu_bar = new JMenuBar (); JMenu menu, submenu, subsubmenu; JMenuItem menu_item; menu = new JMenu ("File"); if (_Application_) { menu_item = new JMenuItem ("New"); menu_item.setMnemonic ('N'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('N', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {New ();}}); menu.add (menu_item); menu_item = new JMenuItem ("Open File ..."); menu_item.setMnemonic ('O'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('O', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_File ();}}); menu.add (menu_item); menu_item = new JMenuItem ("Open URL ..."); menu_item.setMnemonic ('U'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('U', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_URL ();}}); menu.add (menu_item); menu.addSeparator (); } menu_item = new JMenuItem ("Save As ..."); menu_item.setMnemonic ('S'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('S', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Parameters ();}}); menu.add (menu_item); menu_item = new JMenuItem ("Close"); menu_item.setMnemonic ('C'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('W', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { if (_Total_Displays_ == 0 && _Application_) System.exit (0); dispose (); }}); menu.add (menu_item); if (_Application_) { menu_item = new JMenuItem ("Exit"); menu_item.setMnemonic ('X'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('Q', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {System.exit (0);}}); menu.add (menu_item); } menu_bar.add (menu); menu = new JMenu ("View"); menu.add (The_Parameter_Pane.Array_Values_Menu ()); menu_bar.add (menu); return menu_bar; } private void New () { Parameter_View view = new Parameter_View (new Parameter ()); Parameter_View_Locator.Relocate (view, this); view.setVisible (true); } private void Open_File () { if (File_Chooser.showOpenDialog (rootPane) == JFileChooser.APPROVE_OPTION) { File file = File_Chooser.getSelectedFile (); if (! file.isFile ()) { Dialog_Box.Notice ("Can't open file " + file.getAbsolutePath () + '\n' +"No such file.", this); return; } try {CWD (file);} catch (FileNotFoundException exception) {} String canonical_pathname = null; try {canonical_pathname = file.getCanonicalPath ();} catch (Exception exception) {} Parameter parameters = null; try {parameters = new Parameter (file.getPath (), new Parser (file));} catch (PVL_Exception exception) { Dialog_Box.Error ("Unable to load Parameters.\n\n" + ID + '\n' + exception.Message ()); return; } if (canonical_pathname != null && ! canonical_pathname.equals (parameters.Name ())) parameters.Comments (canonical_pathname); The_Parameter_Pane.Parameter (parameters); setTitle (file.getName ()); } } private void Open_URL () { URL_Chooser.setVisible (true); URL url = URL_Chooser.Current_URL (); if (url != null) { InputStream stream = null; try {stream = url.openStream ();} catch (IOException exception) { Dialog_Box.Error ("Unable to open a connection to the URL\n" + url + "\n\n" + exception.getMessage (), this); return; } Parameter parameters = null; try {parameters = new Parameter (url.toString (), new Parser (stream));} catch (PVL_Exception exception) { Dialog_Box.Error ("Unable to load Parameters.\n\n" + ID + '\n' + exception.Message ()); return; } The_Parameter_Pane.Parameter (parameters); Title (url); } } private void Save_Parameters () { if (File_Chooser.showSaveDialog (this) != JFileChooser.APPROVE_OPTION) return; File file = File_Chooser.getSelectedFile (); try {CWD (file);} catch (FileNotFoundException exception) { Dialog_Box.Notice ("The pathname to " + file.getPath () + '\n' +"does not exist.", this); return; } if (file.exists ()) { if (file.isDirectory ()) { Dialog_Box.Notice ("Can't replace directory " + file.getAbsolutePath (), this); return; } if (! Dialog_Box.Confirm ("Replace " + file.getAbsolutePath () + '?', this)) return; } BufferedOutputStream output = null; try { output = new BufferedOutputStream (new FileOutputStream (file), 4096); The_Parameter_Pane.Parameter ().Name (Parser.CONTAINER_NAME).Write (output); } catch (Exception exception) { Dialog_Box.Error ("Couldn't write file " + file.getAbsolutePath () + "\n\n" + exception.getMessage ()); } } /*=***************************************************************************** Application */ /** Creates a Parameter_View displaying the Parameters found in each file listed on the command line.

    @param arguments The command line arguments String array. If no arguments are provided an empty viewer will be created. */ public static void main (String[] arguments) { _Application_ = true; Parameter_View view; if (arguments.length == 0) { view = new Parameter_View (new Parameter ()); view.setLocation (DEFAULT_INITIAL_LOCATION); view.setVisible (true); } else { try { File source_file = null; String canonical_pathname = null; InputStream source_stream; Parameter_View previous_view = null; View_Locator locator = new View_Locator (); for (int count = 0; count < arguments.length; count++) { canonical_pathname = null; if (arguments[count].equals ("-")) { source_stream = System.in; // Prevent the display of the root node. source_file = new File (arguments[count] = Parser.CONTAINER_NAME); } else if ((source_stream = Streams.Get_Stream (arguments[count])) != null) { // Identify the source file. try { URL url = new URL (arguments[count]); source_file = new File (url.getPath ()); if (url.getProtocol ().equals ("file")) { try { canonical_pathname = source_file.getCanonicalPath (); source_file = new File (canonical_pathname); } catch (Exception e) {} } } catch (MalformedURLException except) { source_file = new File (arguments[count]); try { canonical_pathname = source_file.getCanonicalPath (); source_file = new File (canonical_pathname); } catch (Exception e) {} } } else { System.err.println ("Unable to access source: " + arguments[count]); continue; } // Load the parameters. Parameter parameters = null; try {parameters = new Parameter (arguments[count], new Parser (source_stream)) .Comments (canonical_pathname);} catch (PVL_Exception exception) { System.err.println ("Unable to parse the PVL in " + arguments[count] + '\n' + exception.getMessage () + '\n'); continue; } // Construct the viewer. view = new Parameter_View (source_file.getName (), parameters); if (canonical_pathname != null) view.CWD (source_file.getParent ()); if (previous_view == null) view.setLocation (DEFAULT_INITIAL_LOCATION); else locator.Relocate (view, previous_view); previous_view = view; view.setVisible (true); } } catch (Exception exception) { System.err.println (exception.getMessage ()); System.exit (-1); } } if (_Total_Displays_ == 0) System.exit (1); } } // End of class Parameter_View. pirl-2.3.8/PIRL/Viewers/Size_Limits.java0000644000175000017500000001713111742735303017644 0ustar mathieumathieu/* Size_Limits CVS ID: Size_Limits.java,v 1.4 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.Utilities.Integer_Range; import java.awt.Component; import java.awt.event.ComponentListener; import java.awt.event.ComponentEvent; /** Size_Limits limits the size of a component to which it is bound.

    @author Bradford Castalia, idaeim @version 1.4 */ public class Size_Limits implements ComponentListener { public static final String ID = "PIRL.Viewers.Size_Limits (1.4 2012/04/16 06:22:59)"; /** The limits on the size of the component. */ private Integer_Range Width_Limits, Height_Limits; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_LISTENER = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs Size_Limits from a pair of limits.

    @param width_limits The Integer_Range limiting the width of the component. If null, the width will be unlimited. @param height_limits The Integer_Range limiting the height of the component. If null, the width will be unlimited. @see Integer_Range */ public Size_Limits ( Integer_Range width_limits, Integer_Range height_limits ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Composer constructor:\n" +" width_limits - " + width_limits + '\n' +" height_limits - " + height_limits); Width_Limits (width_limits); Height_Limits (height_limits); } /** Constructs Size_Limits with unlimited width and height. */ public Size_Limits () { Width_Limits (null); Height_Limits (null); } /*============================================================================== Accessors */ /** Gets the width limits.

    @return An Integer_Range specifying the width limits. This is a copy of the Integer_Range used by this object; changing its limits will not affect the limits of this object. */ public Integer_Range Width_Limits () {return new Integer_Range (Width_Limits);} /** Gets the minimum width limit.

    @return The minimum width limit. This will be zero if the minimum width is unlimited. The value will never be less than zero. */ public int Min_Width () { long limit = Width_Limits.Minimum (); if (limit <= 0) return 0; if (limit > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int)limit; } /** Gets the maximum width limit.

    @return The maximum width limit. This will be {@link Integer#MAX_VALUE} if the maximum width is unlimited. */ public int Max_Width () { long limit = Width_Limits.Maximum (); if (limit <= 0) return 0; if (limit > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int)limit; } /** Sets the width limits.

    @param width_limits An Integer_Range specifying the limits. @return This Size_Limits. */ public Size_Limits Width_Limits ( Integer_Range width_limits ) { if (width_limits == null) Width_Limits = new Integer_Range (0, Long.MAX_VALUE); else Width_Limits = new Integer_Range (width_limits); return this; } /** Sets the minimum width limit.

    @param min_width The minimum width limit. @return This Size_Limits. */ public Size_Limits Min_Width ( int min_width ) {Width_Limits.Minimum (min_width); return this;} /** Sets the maximum width limit.

    @param max_width The maximum width limit. @return This Size_Limits. */ public Size_Limits Max_Width ( int max_width ) {Width_Limits.Maximum (max_width); return this;} /** Gets the height limits.

    @return An Integer_Range specifying the height limits. This is a copy of the Integer_Range used by this object; changing its limits will not affect the limits of this object. */ public Integer_Range Height_Limits () {return new Integer_Range (Height_Limits);} /** Gets the minimum height limit.

    @return The minimum height limit. This will be zero if the minimum height is unlimited. The value will never be less than zero. */ public int Min_Height () { long limit = Width_Limits.Minimum (); if (limit <= 0) return 0; if (limit > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int)limit; } /** Gets the maximum height limit.

    @return The maximum height limit. This will be {@link Integer#MAX_VALUE} if the maximum height is unlimited. */ public int Max_Height () { long limit = Width_Limits.Maximum (); if (limit <= 0) return 0; if (limit > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int)limit; } /** Sets the height limits.

    @param height_limits An Integer_Range specifying the limits. @return This Size_Limits. */ public Size_Limits Height_Limits ( Integer_Range height_limits ) { if (height_limits == null) Height_Limits = new Integer_Range (0, Long.MAX_VALUE); else Height_Limits = new Integer_Range (height_limits); return this; } /** Sets the minimum height limit.

    @param min_height The minimum height limit. @return This Size_Limits. */ public Size_Limits Min_Height ( int min_height ) {Height_Limits.Minimum (min_height); return this;} /** Sets the maximum height limit.

    @param max_height The maximum height limit. @return This Size_Limits. */ public Size_Limits Max_Height ( int max_height ) {Height_Limits.Maximum (max_height); return this;} /*============================================================================== ComponentListener */ /** The size of the component is constrained to the {@link #Width_Limits() width limits} and {@link #Height_Limits() height limits}.

    @param event The ComponentEvent specifying the component that was resized. */ public void componentResized ( ComponentEvent event ) { if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (">>> Size_Limits.componentResized"); Component component = event.getComponent (); int width = component.getWidth (), height = component.getHeight (); if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" Current: " + width + "x" + height); if (width < Width_Limits.Minimum ()) width = (int)Width_Limits.Minimum (); else if (width > Width_Limits.Maximum ()) width = (int)Width_Limits.Maximum (); if (height < Height_Limits.Minimum ()) height = (int)Height_Limits.Minimum (); else if (height > Height_Limits.Maximum ()) height = (int)Height_Limits.Maximum (); if (width != component.getWidth () || height != component.getHeight ()) { if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println (" Resized: " + width + "x" + height); component.setSize (width, height); } if ((DEBUG & DEBUG_LISTENER) != 0) System.out.println ("<<< Size_Limits.componentResized"); } /** No-op. */ public void componentMoved (ComponentEvent event) {} /** No-op. */ public void componentShown (ComponentEvent event) {} /** No-op. */ public void componentHidden (ComponentEvent event) {} } pirl-2.3.8/PIRL/Viewers/Percent_Bar.java0000644000175000017500000001162211742735303017574 0ustar mathieumathieu/* Percent_Bar PIRL CVS ID: Percent_Bar.java,v 1.3 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JComponent; import javax.swing.UIManager; import java.awt.Color; /** A Percent_Bar provides a bar chart component that is sized to be some percentage of the width or height of its display area.

    @author Bradford Castalia, UA/PIRL @version 1.3 */ public class Percent_Bar extends JComponent { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Percent_Bar (1.3 2012/04/16 06:22:59)"; public static final int TOP = 1, LEFT = 2, BOTTOM = 3, RIGHT = 4, DEFAULT_ORIENTATION = LEFT; private int Orientation = DEFAULT_ORIENTATION; public static final Color DEFAULT_COLOR = Color.BLACK; private Color Bar_Color = DEFAULT_COLOR; private double Percent = 0.0; private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_UI = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Percent_Bar.

    @param percent The percentage of the display width or height occupied by the bar. @param orientation Either VERTICAL or HORIZONTAL. @param color The Color of the bar. */ public Percent_Bar ( double percent, int orientation, Color color ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Percent_Bar.Constructor:" + NL +" Percent: " + percent + NL +" Orientation: " + orientation + NL +" Color: " + color); if (percent < 0.0 || percent > 100.0) throw new IllegalArgumentException (ID + NL + "Invalid percent: " + percent); Percent = percent; if (orientation < 1 || orientation > 4) throw new IllegalArgumentException (ID + NL + "Invalid orientation: " + orientation); Orientation = orientation; if (color == null) color = DEFAULT_COLOR; setForeground (color); UIManager.put ("Percent_BarUI", "Percent_Bar_UI"); setUI (new Percent_Bar_UI ()); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Percent_Bar.Constructor"); } public Percent_Bar () {this (0.0, LEFT, null);} /*============================================================================== Accessors */ public Percent_Bar Percent ( double percent ) { if (percent < 0.0 || percent > 100.0) throw new IllegalArgumentException (ID + NL + "Invalid percent: " + percent); Percent = percent; repaint (); return this; } public double Percent () {return Percent;} public Percent_Bar Orientation ( int orientation ) { if (orientation < 1 || orientation > 4) throw new IllegalArgumentException (ID + NL + "Invalid orientation: " + orientation); Orientation = orientation; repaint (); return this; } public int Orientation () {return Orientation;} /*============================================================================== View */ /** Set the graphical user interface (UI) delegate for the component.

    @param UI A Percent_BarUI object that will paint the component and handle other user interface events. @see JComponent#setUI(ComponentUI) */ public void setUI ( Percent_BarUI UI ) {super.setUI (UI);} /** Set the user interface delegate to the ComponentUI found by the UIManger for this component.

    This component is invalidated.

    @see #getUIClassID() */ public void updateUI () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> Percent_Bar.updateUI"); setUI ((Percent_BarUI)UIManager.getUI (this)); invalidate (); if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< Percent_Bar.updateUI"); } /** Get the ComponentUI class identification for this component.

    @return The String "Percent_BarUI". */ public String getUIClassID () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">-< Percent_Bar.getUIClassID: Percent_BarUI"); return "Percent_BarUI"; } } // End of Percent_Bar class. pirl-2.3.8/PIRL/Viewers/Stream_Viewer.java0000644000175000017500000002152611742735303020170 0ustar mathieumathieu/* Stream_Viewer PIRL CVS ID: Stream_Viewer.java,v 1.8 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.Viewers.Stream_Monitor; import PIRL.Conductor.Stream_Logger; import PIRL.Utilities.Streams; import PIRL.Configuration.*; import javax.swing.JFrame; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.KeyStroke; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Event; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.io.InputStream; import java.io.OutputStreamWriter; /** A Stream_Viewer provides a viewer application attached to stdin.

    This is a demonstration application for the use of a Stream_Monitor with a Stream_Logger.

    @author Bradford Castalia - UA/PIRL @version 1.8 @see Stream_Monitor */ public class Stream_Viewer extends JFrame { /** Class name and version identification. */ public static final String ID = "PIRL.Viewers.Stream_Viewer (1.8 2012/04/16 06:22:59)"; public static final String DEFAULT_CONFIGURATION_PATHNAME = "Stream_Viewer.conf"; private Stream_Monitor Monitor = null; /** Exit status on success. */ public static final int EXIT_SUCCESS = 0; /** Exit status when a command line syntax problem occurs. */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** Exit status when a configuration problem occurs. */ public static final int EXIT_CONFIGURATION_PROBLEM = 1; /*============================================================================== Constructors */ /** Constructs a Stream_Viewer.

    @param title The title String for the window. If null, the default "Stream Viewer" title will be used. @param source An InputStream used as the source of text to be viewed. If null System.in will be used. @param configuration A Configuration (not currently used). */ public Stream_Viewer ( String title, InputStream source, Configuration configuration ) { super ((title == null) ? "Stream Viewer" : title); Configure (configuration); Monitor = new Stream_Monitor (); setJMenuBar (Menus ()); setContentPane (Panels ()); pack (); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); setVisible (true); Monitor .Write (ID + "\n" +Stream_Monitor.ID + "\n\n", Monitor.NOTICE_STYLE) .Auto_Style (true); if (source == null) source = System.in; Stream_Logger stream_logger = new Stream_Logger (null, source, Monitor.Writer ()); stream_logger.start (); } /** Constructs a default Stream_Viewer.

    @see #Stream_Viewer(String, InputStream, Configuration) */ public Stream_Viewer () {this (null, null, null);} /*============================================================================== Configure */ private void Configure ( Configuration configuration ) { // A configuration file might provide auto-styling definitions. } /*============================================================================== Menus */ private JMenuBar Menus () { JMenuBar menu_bar = new JMenuBar (); JMenu menu = Monitor.File_Menu (); JMenuItem menu_item; for (int index = 0; index < menu.getItemCount (); index++) { menu_item = menu.getItem (index); if (menu_item != null) { String text = menu_item.getText (); if (text == null) continue; if (text.startsWith ("Save As")) { menu_item.setMnemonic ('A'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('A', Event.CTRL_MASK)); } else if (text.startsWith ("Save")) { menu_item.setMnemonic ('S'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('S', Event.CTRL_MASK)); } } } menu.addSeparator (); menu_item = new JMenuItem ("Exit"); menu_item.setMnemonic ('X'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('Q', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {System.exit (EXIT_SUCCESS);}}); menu.add (menu_item); menu_bar.add (menu); return menu_bar; } /*============================================================================== Panels */ private JPanel Panels () { JPanel content = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; content.add (Monitor, location); content.setPreferredSize (new Dimension (640, 480)); return content; } /*============================================================================== Application main */ /** Start a Stream_Viewer.

    @param args The application command line arguments. @see #Usage() */ public static void main ( String[] args ) { String title = null, source_name = null, configuration_source = null; InputStream source = null; Configuration configuration = null; for (int count = 0; count < args.length; count++) { if (args[count].length () == 0) continue; if (args[count].charAt (0) == '-' && args[count].length () > 1) { switch (args[count].charAt (1)) { case 'C': // Configuration source case 'c': if (++count == args.length || args[count].charAt (0) == '-') { System.out.println ("Missing configuration source."); Usage (); } if (configuration_source != null) { System.out.println ("Multiple configuration sources specified -\n" +" Old: " + configuration_source + '\n' +" New: " + args[count]); System.exit (EXIT_COMMAND_LINE_SYNTAX); } configuration_source = args[count]; break; case 'T': // Title name case 't': if (++count == args.length || args[count].charAt (0) == '-') { System.out.println ("Missing title name."); Usage (); } if (title != null) { System.out.println ("Multiple titles specified -\n" +" Old: " + title + '\n' +" New: " + args[count]); System.exit (EXIT_COMMAND_LINE_SYNTAX); } title = args[count]; break; default: System.out.println ("Unknown option: " + args[count]); case 'H': // Help case 'h': Usage (); } } else { if (source_name != null) { System.out.println ("Multiple sources specified -\n" + source_name + '\n' +"and\n" + args[count]); Usage (); } source_name = args[count]; } } Configuration.Default_Source (DEFAULT_CONFIGURATION_PATHNAME); try {configuration = new Configuration (configuration_source);} catch (IllegalArgumentException exception) { if (configuration_source != null) { System.out.println (ID + '\n' +"Unable to find the configuration source: " + configuration_source + '\n' + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } // Ignore failure to find default config file. } catch (Configuration_Exception exception) { System.out.println (ID + '\n' +"Unable to load the configuration source.\n" + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } if (source_name == null) { System.out.println ("No input source specified."); Usage (); } if (source_name.equals ("-")) { source_name = "stdin"; source = System.in; } else if ((source = Streams.Get_Stream (source_name)) == null) { System.out.println ("An input stream could not be obtained for " + args[0]); Usage (); } Stream_Viewer viewer = new Stream_Viewer (title, source, configuration); } /** Prints the command line usage syntax.

    Usage: Stream_Viewer <Options>
      Options -
        [-Configuration <source>]
        [-Help]
    

    N.B.This method always results in a System.exit with a status of {@link #EXIT_COMMAND_LINE_SYNTAX}. */ public static void Usage () { System.out.println ( "Usage: Stream_Viewer \n" + " The source may be a filename, URL or '-' for stdin." ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Viewers/Password_Dialog.java0000644000175000017500000001732011742735303020472 0ustar mathieumathieu/* Password_Dialog PIRL CVS ID: Password_Dialog.java,v 1.6 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JLabel; import javax.swing.JButton; import javax.swing.Box; import java.awt.Frame; import java.awt.Container; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; /** A Password_Dialog is a simple dialog box to obtain a password.

    The dialog box contains a prompt ("Password" by default) preceeding a JPasswordField where the password may be entered. Below this is a "Cancel" button in the left corner and an "Accept" button in the right corner.

    @author Bradford Castalia, UA/PIRL @version 1.6 */ public class Password_Dialog extends JDialog { /** Class name and version identification. */ public static final String ID = "PIRL.Viewers.Password_Dialog (1.6 2012/04/16 06:22:59)"; private JPasswordField Password_Field = new JPasswordField ("", 10); private char[] Password = null; /*============================================================================== Constructors */ /** Constructs a Password_Dialog.

    @param title The title String for the dialog window. If null, "Password" will be used by default. @param prompt The text preceding the password input field. If null, "Password" will be used by default. @param parent The Frame with which the dialog is associated, which may be null. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Password_Dialog ( String title, String prompt, Frame parent, boolean modal ) { super (parent, (title == null ? "Password" : title), modal); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); if (prompt == null) prompt = "Password"; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (10, 10, 0, 5); panel.add (new JLabel(prompt + ':'), location); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.insets = new Insets (10, 0, 0, 10); panel.add (Password_Field, location); JPanel button_panel = new JPanel (new GridBagLayout ()); JButton button; button = new JButton ("Cancel"); button.setMnemonic ('C'); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept (false);}}); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.gridwidth = 1; location.weightx = 0.0; location.insets = new Insets (0, 0, 0, 0); button_panel.add (button, location); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; button_panel.add (Box.createHorizontalGlue (), location); button = new JButton ("Accept"); button.setMnemonic ('A'); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept (true);}}); button.setDefaultCapable (true); getRootPane ().setDefaultButton (button); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; button_panel.add (button, location); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.insets = new Insets (10, 10, 10, 10); panel.add (button_panel, location); getContentPane ().add (panel, BorderLayout.CENTER); pack (); } /** Constructs a modal Password_Dialog with the default prompt and no parent.

    @param title The title String for the dialog window. If null, "Password" will be used by default. */ public Password_Dialog ( String title ) {this (title, null, null, true);} /** Constructs a modal Password_Dialog with no parent.

    The dialog will have no parent and will be modal.

    @param title The title String for the dialog window. If null, "Password" will be used by default. @param prompt The text preceding the password input field. If null, "Password" will be used by default. */ public Password_Dialog ( String title, String prompt ) {this (title, prompt, null, true);} /** Constructs a modal Password_Dialog.

    @param title The title String for the dialog window. If null, "Password" will be used by default. @param prompt The text preceding the password input field. If null, "Password" will be used by default. @param parent The Frame with which the dialog is associated, which may be null. */ public Password_Dialog ( String title, String prompt, Frame parent ) {this (title, prompt, parent, true);} /** Constructs a Password_Dialog with no parent.

    @param title The title String for the dialog window. If null, "Password" will be used by default. @param prompt The text preceding the password input field. If null, "Password" will be used by default. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Password_Dialog ( String title, String prompt, boolean modal ) {this (title, prompt, null, modal);} /*============================================================================== Static use. */ public static String Get_Password ( String title, String prompt, Frame parent ) { final Password_Dialog password_dialog = new Password_Dialog (title, prompt, parent); password_dialog.setVisible (true); char[] password = password_dialog.Password (); String password_string = null; if (password != null) { password_string = new String (password); password_dialog.Erase_Password (); } password_dialog.setVisible (false); return password_string; } /*============================================================================== Action */ private void Accept ( boolean accepted ) { if (accepted) Password = Password_Field.getPassword (); else Password = null; setVisible (false); } /*============================================================================== Accessors */ /** Get the password.

    For improved password security {@link #Erase_Password() erase} the password after it has been used.

    @return A char array containing the password characters. This will be null if the Cancel button was pressed the last time the dialog was used, or if the password has been (@link #Erase_Password() erased}. */ public char[] Password () {return Password;} /** Erase the stored password.

    The password stored in the dialog is obliterated. */ public void Erase_Password () { if (Password != null) for (int index = 0; index < Password.length; index++) Password[index] = 0; Password = null; } } pirl-2.3.8/PIRL/Viewers/Percent_BarUI.java0000644000175000017500000000255711742735303020041 0ustar mathieumathieu/* Percent_BarUI PIRL CVS ID: Percent_BarUI.java,v 1.3 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.plaf.ComponentUI; /** A Percent_BarUI is an empty abstract class that provides type identification for a Percent_Bar ComponentUI delegate.

    @see Percent_Bar @see Percent_Bar_UI @author Bradford Castalia, UA/PIRL @version 1.3 */ public abstract class Percent_BarUI extends ComponentUI {} pirl-2.3.8/PIRL/Viewers/Icons.java0000644000175000017500000002312111742735302016457 0ustar mathieumathieu/* Icons PIRL CVS ID: Icons.java,v 1.15 2012/04/16 06:22:58 castalia Exp Copyright (C) 2005-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.ImageIcon; import java.net.URL; import java.net.MalformedURLException; import java.io.File; import java.lang.SecurityException; // For the test stub. import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; /** The Icons class provides static methods to load ImageIcons.

    @author Bradford Castalia @version 1.15 */ public class Icons { //! Class ID. public static final String ID = "PIRL.Utilities.Icons (1.15 2012/04/16 06:22:58)"; //! Default directory name for icon files. public static final String DEFAULT_ICON_DIRECTORY_NAME = "Icons"; private static File Directory = new File (DEFAULT_ICON_DIRECTORY_NAME); private static Class Class_Relative = null; private static final char FILE_SEPARATOR = System.getProperty ("file.separator").charAt (0); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_UTILITIES = 1 << 0, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ // This class only contains static functions. private Icons () {} /*============================================================================== Functions */ /** Loads an ImageIcon from a source.

    If the source is null or the empty String, nothing is done and null is returned.

    First, an attempt is made to construct a URL from the source String. If this succeeds the URL is used to try and load an ImageIcon. No other attempts are made to find the source.

    Next the source is treated as a file pathname. If this pathname is absolute or the source is not {@link #Class_Relative() class-relative} an attempt will be made to load an ImageIcon from the pathname. If the pathname is relative it is prepended with the icon {@link #Directory(String) directory} or {@link #Directory(String, Class) class-relative directory} pathname, whichever is current. If the resultant pathname is for a normal file (rather than a directory) and read access is permitted by any existing security manager the pathname is used to try and load an ImageIcon.

    If an ImageIcon is not yet loaded the pathname from the previous attempt is used to try and locate a system resource. This resource may be found relative to the CLASSPATH or inside a jar file. If this succeeds in producing a URL it is used to try and load an ImageIcon.

    @param source The URL or pathname for the icon image source. @return An ImageIcon, or null if one can not be found or loaded. */ public static ImageIcon Load_Icon ( String source ) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Icons.Load_Icon: " + source); if (source == null || source.length () == 0) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("<<< Icons.Load_Icon"); return null; } ImageIcon image_icon = null; URL url = null; // Treat the source as a URL. try {url = new URL (source);} catch (MalformedURLException exception) {} if (url != null) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Using URL " + url); image_icon = new ImageIcon (url); } else { // Treat the source as a file pathname. boolean try_file = (Class_Relative == null); File file = new File (source); if (file.isAbsolute ()) try_file = true; else file = new File (Directory.getPath () + FILE_SEPARATOR + source); if (try_file) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Trying file " + file); if (file.canRead () && file.isFile ()) { try {image_icon = new ImageIcon (file.getPath ());} catch (SecurityException exception) {} } else if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Not readable"); } if (image_icon == null) { // Treat the source as a system resource. if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Trying system resource at " + file.getPath ()); url = ClassLoader.getSystemResource (file.getPath ()); if (url != null) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Using system resource URL " + url); image_icon = new ImageIcon (url); } } } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("<<< Icons.Load_Icon: " + ((image_icon == null) ? "not " : "") + "found"); return image_icon; } /** Sets the name of the directory where icon image files are to be found.

    N.B.: The directory pathname is marked as being not {@link #Class_Relative() class-relative}.

    @param directory_path The pathname for the directory. If null or empty the {@link #DEFAULT_ICON_DIRECTORY_NAME} will be used. @return The previous directory pathname. */ public static String Directory ( String directory_path ) { String previous_directory_path = Directory.getPath (); if (directory_path == null || directory_path.length () == 0) Directory = new File (DEFAULT_ICON_DIRECTORY_NAME); else Directory = new File (directory_path); Class_Relative = null; return previous_directory_path; } /** Sets the name of the directory where icon image files may be found relative to the location of a class.

    If the directory name is null or empty the {@link #DEFAULT_ICON_DIRECTORY_NAME} will be used. If the directory name is an absolute pathname, the class location is not used. Otherwise the directory pathname to the class location is prepended to the directory name and the resultant {@link #Directory() directory pathname} is marked as being {@link #Class_Relative() class-relative}.

    @param directory_path The pathname for the icons directory. If this is a relative pathname it will be relative to the location of the class. @param the_class The Class location to use as the base for a relative directory pathname. Ignored if null. @return The previous directory pathname. */ public static String Directory ( String directory_path, Class the_class ) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Icons.Directory: " + directory_path); String previous_directory_path = Directory (directory_path); if (the_class != null && ! Directory.isAbsolute ()) { String class_name = the_class.getName (); if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" Class name: " + class_name); int index = class_name.lastIndexOf ('.'); if (index >= 0) /* Get the CLASSPATH relative driectory path to the icons by removing the final class name segment to get the package name, replacing the Java '.' segment delimiters with file separators, and appending the directory path afer a file separator. */ Directory = new File (class_name.substring (0, index) .replace ('.', FILE_SEPARATOR) + FILE_SEPARATOR + Directory.getPath ()); Class_Relative = the_class; } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println ("<<< Icons.Directory: " + Directory); return previous_directory_path; } /** Gets the Class to which the current {@link #Directory() directory} is relative.

    @return The last Class used to set a {@link #Directory(String, Class) class-relative} directory pathname. This will be null if the current {@link #Directory() directory pathname} is not class-relative. */ public static Class Class_Relative () {return Class_Relative;} /** Gets the pathname of the directory where icon image files are to be found.

    @return The directory name. */ public static String Directory () {return Directory.getPath ();} /*============================================================================== Test stub */ public static void main (String arguments[]) { System.out.println (ID); if (arguments.length > 0) { System.out.println ("Default Directory: " + Directory ()); ImageIcon icon = Load_Icon (arguments[0]); if (icon == null) { System.out.println ("No icon at " + arguments[0]); Class this_class = null; try {this_class = Class.forName ("PIRL.Viewers.Icons");} catch (ClassNotFoundException exception) { System.out.println ("Icons class not found!"); System.exit (1); } Directory (Directory (), this_class); System.out.println ("Class-relative Directory: " + Directory ()); icon = Load_Icon (arguments[0]); } if (icon != null) { final JFrame frame = new JFrame ("Icon " + arguments[0]); frame.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent e) {System.exit (0);}}); JPanel content = new JPanel (); frame.setContentPane (content); JLabel label = new JLabel (icon); content.add (label); frame.pack (); frame.setVisible (true); } else System.out.println ("Icon file not found."); } else System.out.println ("Usage: Icons "); } } pirl-2.3.8/PIRL/Viewers/Draggable_Rows.java0000644000175000017500000010721611742735302020276 0ustar mathieumathieu/* Draggable_Rows idaeim CVS ID: Draggable_Rows.java,v 1.7 2012/04/16 06:22:58 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JLayeredPane; import javax.swing.SizeRequirements; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputListener; import javax.swing.event.MouseInputAdapter; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionEvent; import java.awt.LayoutManager2; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Point; import java.awt.Insets; import java.awt.Color; import java.awt.event.MouseEvent; import java.util.Vector; import java.util.Iterator; /** Draggable_Rows manages the display of Components as a stack of draggable rows.

    A Draggable_Rows_Layout is used to maintain the display of the components as a single column of rows. All components, regardless of the layer they have been assigned to, are displayed in top-to-bottom order. Any components may be used as row components. These components may have their own functionality, including subcomponents.

    A MouseInputListener is attached to the base pane and to each row component and any subcomponents it might have. Whenever a mouse button is pressed in the pane the row component that it falls within is designated the {@link #Selected_Row() selected row} and any {@link #Add_Selection_Listener(ListSelectionListener) registered list selection listeners} are notified. If the shift key modifier is also used then the selected row may be dragged vertically to a new row position. Any {@link #Add_Row_Moved_Listener(ChangeListener) registered row move listeners} are notified whenever the position of a row changes.

    @author Bradford Castalia, idaeim studio @version 1.7 @see JLayeredPane */ public class Draggable_Rows extends JLayeredPane { /** Class name and version identification. */ public static final String ID = "PIRL.Viewers.Draggable_Rows (1.7 2012/04/16 06:22:58)"; //! The width of the panel if it is empty. private static final int EMPTY_WIDTH = 150; //! The LayoutManager for the panel. public Draggable_Rows_Layout Layout_Manager = new Draggable_Rows_Layout (); //! Background color for selected panel (actiated row) private Color Background_Color = null; //! The list of listeners to be notified when a row location changes. private Vector Row_Moved_Listeners = new Vector (); //! The list of listeners to be notified when a row selection changes. private Vector Selection_Listeners = new Vector (); //! The currently selected row component. public Component Selected_Row = null; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ROWS = 1 << 1, DEBUG_LAYOUT = 1 << 2, DEBUG_SIZES = 1 << 3, DEBUG_DRAG = 1 << 4, DEBUG_DRAGGING = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; // DEBUG = DEBUG_ALL & ~DEBUG_DRAGGING; /*============================================================================== Constructors */ /** Constructs a Draggable_Rows pane containing a list of row components.

    The row components will be be displayed top-to-bottom in the order they occur in the list.

    @param rows A Vector of Components to be added to the pane. */ public Draggable_Rows ( Vector rows ) { if ((DEBUG & (DEBUG_CONSTRUCTOR | DEBUG_ROWS)) != 0) System.out.println (">>> Draggable_Rows: " + ((rows == null) ? 0 : rows.size ()) + " rows"); setLayout (Layout_Manager); if (rows != null) { // Add the row components to the pane. for (int index = 0; index < rows.size (); index++) { if ((DEBUG & (DEBUG_CONSTRUCTOR | DEBUG_ROWS)) != 0) { ((Component)rows.get (index)).setName ("Row " + index); System.out.println (" Add row " + index + ": Row " + index); } add ((Component)rows.get (index), JLayeredPane.DEFAULT_LAYER); } } // Register the mouse events handler with the pane. addMouseListener (Mouse_Events_Handler); addMouseMotionListener (Mouse_Events_Handler); if ((DEBUG & (DEBUG_CONSTRUCTOR | DEBUG_ROWS)) != 0) System.out.println ("<<< Draggable_Rows"); } /** Constructs an empty Draggable_Rows pane. */ public Draggable_Rows () {this (null);} /*============================================================================== Manipulators */ /** Sets the currently selected row component.

    If the specified component is a row component it is selected. Each {@link #Add_Selection_Listener registered selection listener} is notified of the change. The ListSelectionEvent sent to each listener will contain this Draggable_Rows as the source and the index of the selected component (for both first and last index). However, because the index of a component might change, (@link #Selected_Row()} is the best way to obtain the currently selected row component.

    @param component The row component to be selected. If the component is not a row component, or is null, nothing is done. @return This Draggable_Rows. */ public Draggable_Rows Selected_Row ( Component component ) { final int index = getIndexOf (component); if (index >= 0 && component != Selected_Row) { if ((DEBUG & DEBUG_ROWS) != 0) System.out.println (">-< Draggable_Rows.Selected_Row: " + component.getName () + " at index " + getIndexOf (component)); Selected_Row = component; if (! Selection_Listeners.isEmpty ()) javax.swing.SwingUtilities.invokeLater (new Runnable () { public void run () { // Notify all selection listeners. if ((DEBUG & DEBUG_ROWS) != 0) System.out.println ("--- Draggable_Rows.Selected_Row: " +"Notifying Selection_Listeners"); Iterator listeners = Selection_Listeners.iterator (); while (listeners.hasNext ()) ((ListSelectionListener)listeners.next ()).valueChanged (new ListSelectionEvent (this, index, index, false)); }}); } return this; } /** Gets the currently selected row component.

    @return The selected row Component. This will be null if not component is selected. @see #Selected_Row(Component) */ public Component Selected_Row () {return Selected_Row;} /** Adds a list selection listener.

    Each registered listener is notified whenever the row selection changes.

    @param listener A ListSelectionListener that will have its valueChanged method called whenever the row selection changes. A duplicate or null listener is not added. @return This Draggable_Rows. @see ListSelectionListener */ public Draggable_Rows Add_Selection_Listener ( ListSelectionListener listener ) { if (listener != null && ! Selection_Listeners.contains (listener)) Selection_Listeners.add (listener); return this; } /** Removes a list selection listener.

    @param listener The ListSelectionListener to be removed. @return true If the listener was removed; false if it was not registered. @see #Add_Selection_Listener(ListSelectionListener) */ public boolean Remove_Selection_Listener ( ChangeListener listener ) {return Selection_Listeners.remove (listener);} /** Adds a row moved listener.

    Each registered listener is notified whenever a row position changes.

    @param listener A ChangeListener that will have its stateChanged method called whenever a row position changes. A duplicate or null listener is not added. @return This Draggable_Rows. @see ChangeListener */ public Draggable_Rows Add_Row_Moved_Listener ( ChangeListener listener ) { if (listener != null && ! Row_Moved_Listeners.contains (listener)) Row_Moved_Listeners.add (listener); return this; } /** Removes a row moved listener.

    @param listener The ChangeListener to be removed. @return true If the listener was removed; false if it was not registered. @see #Add_Row_Moved_Listener(ChangeListener) */ public boolean Remove_Row_Moved_Listener ( ChangeListener listener ) {return Row_Moved_Listeners.remove (listener);} /** Removes an indexed row component from the pane.

    Before the component is removed from the pane it, and any subcomponents, has this pane's mouse event listener removed. If the component being removed is the currently selected row, the {@link #Selected_Row() selected row} is reset to null.

    @param index The index of the component to remove. An invalid index is ignored. */ public void remove ( int index ) { if (index >= 0 && index < getComponentCount ()) { Component component = getComponent (index); Remove_Mouse_Listeners (component); if (Selected_Row == component) Selected_Row = null; super.remove (index); } } /** Adds a component to the pane.

    Before the component is added, this pane's mouse event listener is added to the component and any subcomponents it might have. The listener is not added if the component is already contained in this pane.

    @param component The Component to be added. @param constraints Layout contraints. This is expected to be a layer number Integer. @param index The position in the container's list at which to insert the component, where -1 means append to the end. */ protected void addImpl ( Component component, Object constraints, int index ) { if ((DEBUG & DEBUG_LAYOUT) != 0) System.out.println (">>> Draggable_Rows.addtImpl"); if (getIndexOf (component) < 0) { // New component. if ((DEBUG & DEBUG_LAYOUT) != 0) System.out.println (" Add_Mouse_Listeners"); Add_Mouse_Listeners (component); } super.addImpl (component, constraints, index); if ((DEBUG & DEBUG_LAYOUT) != 0) System.out.println ("<<< Draggable_Rows.addImpl"); } //------------------------------------------------------------------------------ private void Add_Mouse_Listeners ( Component component ) { if (component == null) return; component.addMouseListener (Mouse_Events_Handler); component.addMouseMotionListener (Mouse_Events_Handler); if (component instanceof Container) { int index = ((Container)component).getComponentCount (); while (--index >= 0) Add_Mouse_Listeners (((Container)component).getComponent (index)); } } private void Remove_Mouse_Listeners ( Component component ) { if (component == null) return; component.removeMouseListener (Mouse_Events_Handler); component.removeMouseMotionListener (Mouse_Events_Handler); if (component instanceof Container) { int index = ((Container)component).getComponentCount (); while (--index >= 0) Remove_Mouse_Listeners (((Container)component).getComponent (index)); } } /*=***************************************************************************** Draggable_Rows_Layout */ /** Draggable_Rows_Layout is a LayoutManager for a vertical stack of row Components.

    @see LayoutManager2 */ public class Draggable_Rows_Layout implements LayoutManager2 { //! Horizontal alignment coeficient: 0.0 left, 1.0 right, 0.5 center, etc. private float Horizontal_Alignment = 0.0f; /** Size constraint triplets - minimum, preferred, maximum - for each component dimension - width and height. */ private SizeRequirements[] Component_Widths = new SizeRequirements[0], Component_Heights = new SizeRequirements[0]; /** Size constraint triplets - minimum, preferred, maximum - for the container dimension - width and height. */ private SizeRequirements Container_Widths = null, Container_Heights = null; /*============================================================================== Constructors */ /** Constructs a Draggable_Rows_Layout */ Draggable_Rows_Layout () { if ((DEBUG & (DEBUG_CONSTRUCTOR | DEBUG_LAYOUT)) != 0) System.out.println (">-< Draggable_Rows_Layout"); } /*============================================================================== Accessors */ /** Sets the horizontal row alignment factor.

    An alignment of 0.0 aligns the rows against their left sides; 1.0 aligns against the rights sides; 0.5 aligns rows along their centers; etc.

    The initial, default, alignment factor is 0.0.

    @param alignment The horizontal alignment factor. @return This Draggable_Rows_Layout. */ public Draggable_Rows_Layout setLayoutAlignmentX ( float alignment ) { Horizontal_Alignment = Math.max (0.0f, Math.min (alignment, 1.0f)); return this; } /*============================================================================== Manipulators */ /** Calculates a container layout size.

    If the container is empty or its size requirements have not been {@link #invalidateLayout(Container) invalidated} since they were last set, then nothing is done.

    A set of width and height {@link SizeRequirements} is maintained for each component and for the entire container. The components' minimum, preferred and maximum sizes are used; the {@link #setLayoutAlignmentX(float) horizontal alignment factor} is applied. Components that are not visible have no affect on the layout.

    The container's width size requirements are based on the {@link SizeRequirements#getAlignedSizeRequirements(SizeRequirements[]) aligned size requirements} of the components; the height size requirements are based on the {@link SizeRequirements#getTiledSizeRequirements(SizeRequirements[]) tiled size requirements} of the components.

    @param container The container associated with this layout manager. */ protected void Update_Sizes ( Container container ) { if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (">>> Draggable_Rows_Layout.Update_Sizes:"); int total_rows = container.getComponentCount (); if (total_rows == 0 || Container_Widths != null) { if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println ("<<< Draggable_Rows_Layout.Update_Sizes:"); return; } if (Component_Widths.length != total_rows) { Component_Widths = new SizeRequirements[total_rows]; Component_Heights = new SizeRequirements[total_rows]; } for (int row = 0; row < total_rows; row++) { Component component = container.getComponent (row); Dimension minimum = component.getMinimumSize (), preferred = component.getPreferredSize (), maximum = component.getMaximumSize (); if (! component.isVisible ()) // Invisible component. minimum.height = minimum.width = preferred.height = preferred.width = maximum.height = maximum.width = 0; Component_Widths[row] = new SizeRequirements (minimum.width, preferred.width, maximum.width, Horizontal_Alignment); Component_Heights[row] = new SizeRequirements (minimum.height, preferred.height, maximum.height, 0.0f); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (" Component " + row + ": " + component.getName () + " is " + (component.isVisible () ? "" : " not") + " visible\n" +" Component_Widths = " + Component_Widths[row] + '\n' +" Component_Heights = " + Component_Heights[row]); } Container_Widths = // Aligned, left side. SizeRequirements.getAlignedSizeRequirements (Component_Widths); Container_Heights = // Tiled, vertically. SizeRequirements.getTiledSizeRequirements (Component_Heights); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println ("<<< Draggable_Rows_Layout.Update_Sizes:\n" +" Container_Widths = " + Container_Widths + '\n' +" Container_Heights = " + Container_Heights); } /** Convenience constants for specifying which {@link #Layout_Size(Container, int) layout size} dimensions to calculate. */ protected static final int MINIMUM_DIMENSION = -1, PREFERRED_DIMENSION = 0, MAXIMUM_DIMENSION = 1; /** Gets the dimensions for a container suitable for laying out the components it contains.

    The container, if it is not empty, has the preferred, minimum or maximum size, depending on which dimension is specified, of each component it contains examined. The container width is the maximum value of the component widths, or the {@link #EMPTY_WIDTH} if there are no components, plus the left and right insets amount. The container height is the sum of the heights of all visible components, or zero if there are no components, plus the top and bottom insets amount.

    @param container The container associated with this layout manager. @param which_dimension If the {@link #MINIMUM_DIMENSION}, the minimum sizes will be calculated; if the {@link #PREFERRED_DIMENSION}, the preferred size is determined; if the {@link #MAXIMUM_DIMENSION} the maximum size. @return The requested Dimension for the container. @see #Update_Sizes(Container) @see #minimumLayoutSize(Container) @see #preferredLayoutSize(Container) @see #maximumLayoutSize(Container) */ protected Dimension Layout_Size ( Container container, int which_dimension ) { if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (">>> Draggable_Rows_Layout.Layout_Size: " + which_dimension + " (" + ((which_dimension == 0) ? "preferred" : ((which_dimension < 0) ? "minimum" : "maximum")) + ")"); Dimension size = new Dimension (EMPTY_WIDTH, 0); if (container.getComponentCount () != 0) { synchronized (this) { Update_Sizes (container); size = new Dimension ((which_dimension == 0) ? Container_Widths.preferred : ((which_dimension < 0) ? Container_Widths.minimum : /*which_dimension > 0)*/ Container_Widths.maximum), ((which_dimension == 0) ? Container_Heights.preferred : ((which_dimension < 0) ? Container_Heights.minimum : /*which_dimension > 0)*/ Container_Heights.maximum))); } } if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (" Size - " + size); // Add the insets boundary size. Insets insets = container.getInsets (); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (" Container insets - " + insets.left + "l, " + insets.right + "r, " + insets.top + "t, " + insets.bottom + "b"); // Clip to max, no integer overflow. size.width = (int)Math.min ((long)size.width + (long)insets.left + (long)insets.right, Integer.MAX_VALUE); size.height = (int)Math.min ((long)size.height + (long)insets.top + (long)insets.bottom, Integer.MAX_VALUE); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println ("<<< Draggable_Rows_Layout.Layout_Size: " + size); return size; } // LayoutManager interface //////////////////////////////////////////////////// /** Layout the container components as stacked rows.

    All components of the container are distributed in top-to-bottom order as they occur in the container's component list. The SizeRequirements for the components are first {@link #Update_Sizes(Container) updated}. The the bounds of each component are then determined using horizontally alignment into a single column and vertical top-to-bottom distribution into continguous rows.

    @param container The container associated with this layout manager. */ public void layoutContainer ( Container container ) { if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (">>> Draggable_Rows_Layout.layoutContainer"); int total_rows = container.getComponentCount (); if (total_rows == 0) { if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println ("<<< Draggable_Rows_Layout.layoutContainer: no rows"); return; } Dimension dimension = container.getSize (); Insets insets = container.getInsets (); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (" Container size - " + dimension + '\n' +" insets - " + insets); // Shrink by boundary removal. dimension.width -= insets.left + insets.right; dimension.height -= insets.top + insets.bottom; // Determine the component positions. int[] x_offsets = new int[total_rows], widths = new int[total_rows], y_offsets = new int[total_rows], heights = new int[total_rows]; synchronized (this) { Update_Sizes (container); // Horizontally aligned. SizeRequirements.calculateAlignedPositions (dimension.width, Container_Widths, Component_Widths, x_offsets, widths); // Vertically tiled (distributed). SizeRequirements.calculateTiledPositions (dimension.height, Container_Heights, Component_Heights, y_offsets, heights); } // Position the components. for (int row = 0; row < total_rows; row++) { Component component = container.getComponent (row); if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println (" Component " + row + ": " + component.getName () + " setBounds (" + (int)Math.min ((long)insets.left + (long)x_offsets[row], Integer.MAX_VALUE) + "x, " + (int)Math.min ((long)insets.top + (long)y_offsets[row], Integer.MAX_VALUE) + "y, " + widths[row] + "w, " + heights[row] + "h)"); component.setBounds ( // Horizontal (clip to max, no integer overflow). (int)Math.min ((long)insets.left + (long)x_offsets[row], Integer.MAX_VALUE), // Vertical (clip to max, no integer overflow). (int)Math.min ((long)insets.top + (long)y_offsets[row], Integer.MAX_VALUE), widths[row], heights[row] ); } if ((DEBUG & (DEBUG_LAYOUT | DEBUG_SIZES)) != 0) System.out.println ("<<< Draggable_Rows_Layout.layoutContainer"); } /** Gets the minumum layout dimensions for the container.

    @param container The container associated with this layout manager. @return The requested Dimension for the container. @see #Layout_Size(Container, int) */ public Dimension minimumLayoutSize ( Container container ) {return Layout_Size (container, MINIMUM_DIMENSION);} /** Gets the preferred layout dimensions for the container.

    @param container The container associated with this layout manager. @return The requested Dimension for the container. @see #Layout_Size(Container, int) */ public Dimension preferredLayoutSize ( Container container ) {return Layout_Size (container, PREFERRED_DIMENSION);} //! Ignored. public void addLayoutComponent (String name, Component component) {} //! Ignored. public void removeLayoutComponent (Component component) {} // LayoutManager2 ///////////////////////////////////////////////////////// /** Gets the maximum layout dimensions for the container.

    @param container The container associated with this layout manager. @return The requested Dimension for the container. @see #Layout_Size(Container, int) */ public Dimension maximumLayoutSize ( Container container ) {return Layout_Size (container, MAXIMUM_DIMENSION);} /** Gets the horizontal alignment factor for the layout.

    @param container The container associated with this layout manager. @return The horizontal alignment factor. @see #setLayoutAlignmentX(float) */ public float getLayoutAlignmentX (Container container) {return Horizontal_Alignment;} /** Gets the vertical alignment factor for the layout.

    @param container The container associated with this layout manager. @return The horizontal alignment factor which is always 0.0. */ public float getLayoutAlignmentY (Container container) {return 0.0f;} //! Ignored. public void addLayoutComponent ( Component component, Object constraints ) {addLayoutComponent (component.getName (), component);} /** The layout constraints for the container are invalidated.

    The next time a {@link #layoutContainer(Container) layout} is to occur the layout constraints will be {@link #Update_Sizes(Container) updated}.

    This method is called by AWT when the invalidate method is called on the Container. Since the invalidate method may be called asynchronously to the event thread, this method may be called asynchronously.

    @param container The affected container. */ public synchronized void invalidateLayout ( Container container ) {Container_Widths = null;} }; // End of Draggable_Rows_Layout class. /*============================================================================== Mouse events handler for dragging. */ private MouseInputListener Mouse_Events_Handler = new MouseInputAdapter () { //! Row component being dragged. private Component Row = null; //! The row's layer. private int Row_Layer; //! Position of the row in the layer. private int Row_Position; //! Location of the row in the container when dragging started. private Point Row_Origin = new Point (); //! Y offset of the mouse in the container when dragging started. private int Mouse_Y_Offset; //! Maximum y row drap location in the container. private int Max_Y; //! Flag to signal when the handlers are ready to accept events. private boolean Ready = true; // MouseInputListener ///////////////////////////////////////////////////////// /** Handle a mouse button press event.

    The row component where the mouse press occured becomes the {@link #Selected_Row(Component) selected row}. If the event handlers are not ready (row move notifications are pending), the Shet key modifier is not set, there are less than two row components, or no component row was selected then the event is ignored. Otherwise the event is consumed (to prevent other row component actions) and the row dragging state variables are set. The row component is elevated to the DRAG_LAYER so it will be displayed over the other row components while it is being moved.

    @param event A MouseEvent. */ public void mousePressed ( MouseEvent event ) { Point mouse_point = this_Point (event); if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (">>> Draggable_Rows.Mouse_Events_Handler.mousePressed: " + mouse_point); Selected_Row (Row = getComponentAt (mouse_point)); if (! Ready || ! event.isShiftDown () || getComponentCount () < 2 || Row == null) { if ((DEBUG & DEBUG_DRAG) != 0) System.out.println ("<<< Draggable_Rows.Mouse_Events_Handler.mousePressed: ignored\n" +" " + (Ready ? (event.isShiftDown () ? ((getComponentCount () < 2) ? ((Row == null) ? "No row component!" : "No reason!?!") : "Insufficient components.") : "No Shift modifier.") : "Not Ready; Row_Moved_Event notifications pending.")); Row = null; return; } Mouse_Y_Offset = mouse_point.y; Row.getLocation (Row_Origin); Row_Layer = getLayer (Row); Row_Position = getPosition (Row); Max_Y = getHeight () - Row.getHeight (); // The event is meant to initiate a row drag; consume it. event.consume (); if ((DEBUG & DEBUG_DRAG) != 0) { int row_index = getIndexOf (Row); System.out.println (" Source: " + Row.getName () + '\n' +" Container screen location = " + getLocationOnScreen () + '\n' +" Row screen location = " + Row.getLocationOnScreen () + '\n' +" Row container location = " + Row_Origin + '\n' +" Row layer = " + Row_Layer + '\n' +" Row layer position = " + Row_Position + '\n' +" Row index = " + getIndexOf (Row) + '\n' +" Current positions -"); for (int index = 0; index < getComponentCount (); index++) System.out.println (" Component " + index + ": " + getComponent (index).getName ()); } if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (" setLayer (Source, layer " + JLayeredPane.DRAG_LAYER.intValue () + ")"); /* Elevate the drag row component to the drag layer. N.B.: Moving the component to the drag layer moves it to position 0. All other components shift down in position. */ setLayer (Row, JLayeredPane.DRAG_LAYER.intValue ()); if ((DEBUG & DEBUG_DRAG) != 0) { System.out.println (" Drag positions -"); for (int index = 0; index < getComponentCount (); index++) System.out.println (" Component " + index + ": " + getComponent (index).getName ()); System.out.println ("<<< Draggable_Rows.Mouse_Events_Handler.mousePressed"); } } /** Handle a mouse drag event.

    The event is ignored if there is no active row component being dragged or the event handlers are not ready (row move notifications are pending). Otherwise the row component being dragged is moved to the new vertical location in the container relative to the mouse event location. The location is limited to keep the row component within the bounds of the container.

    @param event A MouseEvent. */ public void mouseDragged ( MouseEvent event ) { if (Row == null || ! Ready) return; if ((DEBUG & DEBUG_DRAG) != 0 && (DEBUG & DEBUG_DRAGGING) != 0) System.out.println (">>> Draggable_Rows.Mouse_Events_Handler.mouseDragged: " + this_Point (event)); int y = Math.max (0, Math.min (Max_Y, Row_Origin.y + this_Point (event).y - Mouse_Y_Offset)); if ((DEBUG & DEBUG_DRAG) != 0 && (DEBUG & DEBUG_DRAGGING) != 0) System.out.println (" Row.getLocationOnScreen = " + Row.getLocationOnScreen () + '\n' +" Row.getLocation = " + Row.getLocation () + '\n' +" Row.setLocation (" + Row_Origin.x + ", " + y + ")"); Row.setLocation (Row_Origin.x, y); if ((DEBUG & DEBUG_DRAG) != 0 && (DEBUG & DEBUG_DRAGGING) != 0) System.out.println ("<<< Draggable_Rows.Mouse_Events_Handler.mouseDragged"); } /** Handle a mouse button released event.

    The event is ignored if there is no active row component being dragged or the event handlers are not ready (row move notifications are pending). Otherwise the {@link #Insert_Position(int) insert position} of the mouse location is determined and the row is put back in its original layer at this position. If the location of the row as moved at all (not just its position) the layout is invalidated and revalidation is scheduled on the event queue.

    If the row component has changed position and there are any {@link #Add_Row_Moved_Listener(ChangeListener) registered row move listeners} the event handlers are set to be not ready and notification of all the row move listeners is scheduled on the event queue. When all notifications are complete the event handlers are put back in their ready state and dragging row is deactivated.

    @param event A MouseEvent. */ public void mouseReleased ( MouseEvent event ) { if (Row == null || ! Ready) return; Point mouse_point = this_Point (event); if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (">>> Draggable_Rows.Mouse_Events_Handler.mouseReleased: " + mouse_point); int insert_position = Insert_Position (Row.getY () + (Row.getHeight () >> 1)); if ((DEBUG & DEBUG_DRAG) != 0) { System.out.println (" From " + Row_Position + " to " + insert_position + '\n' +" Pre-move positions -"); for (int index = 0; index < getComponentCount (); index++) System.out.println (" Component " + index + ": " + getComponent (index).getName ()); System.out.println (" setLayer (Row, layer " + Row_Layer + ", position " + insert_position + ")"); } setLayer (Row, Row_Layer, insert_position); if ((DEBUG & DEBUG_DRAG) != 0) { System.out.println (" Post-move positions -"); for (int index = 0; index < getComponentCount (); index++) System.out.println (" Component " + index + ": " + getComponent (index).getName ()); } if (mouse_point.y != Mouse_Y_Offset) { // The row has been moved. invalidate (); javax.swing.SwingUtilities.invokeLater (new Runnable () { public void run () {validate ();} }); } if (insert_position < 0) insert_position = getComponentCount () - 1; if (insert_position != Row_Position && ! Row_Moved_Listeners.isEmpty ()) { Ready = false; final int position = insert_position; javax.swing.SwingUtilities.invokeLater (new Runnable () { public void run () { // Notify all change listeners. if ((DEBUG & DEBUG_DRAG) != 0) System.out.println ("--- Draggable_Rows.Mouse_Events_Handler.mouseReleased: " +"Notifying Row_Moved_Listeners"); Iterator listeners = Row_Moved_Listeners.iterator (); while (listeners.hasNext ()) ((ChangeListener)listeners.next ()).stateChanged (new Row_Moved_Event (Row, Row_Position, position)); // Drag completed. Ready = true; Row = null; }}); if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (" Row_Moved_Listeners notification scheduled."); } else Row = null; if ((DEBUG & DEBUG_DRAG) != 0) System.out.println ("<<< Draggable_Rows.Mouse_Events_Handler.mouseReleased"); } /** Gets the insert position for a row.

    Each component that is showing is checked to see if the reference y position is within the component. If so, the index of that component is the returned position. This assumes that all components are laid out in top-to-bottom order, which is the case for a Draggable_Rows_Layout.

    @param reference_y The vertical reference location for the insertion. @return The index of the showing component that contains the reference y location. */ private int Insert_Position ( int reference_y ) { if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (">>> Draggable_Rows.Mouse_Events_Handler.Insert_Position: " + reference_y); int component_y = getInsets ().top, total_rows = getComponentCount (), index; for (index = 0; index < total_rows; index++) { Component component = getComponent (index); if (component.isShowing ()) { if (component != Row) { int top = component.getY (); if (top < 0) // Component extends above the container top. component_y -= top; } component_y += component.getHeight (); if ((DEBUG & DEBUG_DRAG) != 0) System.out.println (" " + index + ": " + component_y); if (reference_y < component_y) // The reference falls within the component bounds. break; } } if (index == total_rows) index = -1; if ((DEBUG & DEBUG_DRAG) != 0) System.out.println ("<<< Draggable_Rows.Mouse_Events_Handler.Insert_Position: " + index); return index; } }; // End of Mouse_Events_Handler. //------------------------------------------------------------------------------ /** A Row_Moved_Event carries the information that a component row has moved from one position to another in the pane. */ public class Row_Moved_Event extends ChangeEvent { //! The position from which the component moved. public int From; //! The position to which the component moved. public int To; /** Constructs a Row_Moved_Event.

    @param row The Component row that has moved. @param from The row position from which the component moved. @param to The row position to which the component moved. */ Row_Moved_Event ( Component row, int from, int to ) { super (row); From = from; To = to; } }; // End of Row_Moved_Event. /*============================================================================== Helpers */ /** Gets the location of a mouse event relative to the coordinates of this pane.

    The event's location point is translated from the coordinates of the event's component to the coordinates of this pane.

    @param event A MouseEvent. @return The mouse event location in this pane's coordinate space. @see SwingUtilities#convertPoint(Component, Point, Component) */ public Point this_Point ( MouseEvent event ) { Component component = event.getComponent (); if (component == this) return event.getPoint (); return SwingUtilities.convertPoint (component, event.getPoint (), this); } /** Gets the location of a mouse event relative to the coordinates of the display screen.

    The event's location point is translated from the coordinates of the event's component to the coordinates of the display screen.

    @param event A MouseEvent. @return The mouse event location in the display screen's coordinate space. @see SwingUtilities#convertPointToScreen(Point, Component) */ public static Point Screen_Point ( MouseEvent event ) { Point point = event.getPoint (); SwingUtilities.convertPointToScreen (point, event.getComponent ()); return point; } } // End of Draggable_Rows class. pirl-2.3.8/PIRL/Viewers/Projection_Viewer0000755000175000017500000000100011115133462020105 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the Java Projection_Viewer application. # # CVS ID: Projection_Viewer,v 1.1 2008/12/02 04:18:58 castalia Exp $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $CLASSPATH = $ENV{"CLASSPATH"}; if ($PIRL_JAVA_HOME) { if ($CLASSPATH) { $CLASSPATH = "$PIRL_JAVA_HOME:$CLASSPATH"; } else { $CLASSPATH = $PIRL_JAVA_HOME; } } @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Viewers.Projection_Viewer", @ARGV; exec @Arguments; pirl-2.3.8/PIRL/Viewers/View_Locator.java0000644000175000017500000011632211742735303020010 0ustar mathieumathieu/* View_Locator PIRL CVS ID: View_Locator.java,v 1.15 2012/04/16 06:22:59 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import java.awt.Window; import java.awt.Rectangle; import java.awt.Point; import java.awt.Insets; import java.awt.Toolkit; // For Select_LAF import javax.swing.UIManager; import javax.swing.JFrame; import javax.swing.JDialog; /** A View_Locator is used to manage the location of a Window view on the Display relative to a base location.

    @author Bradford Castalia, UA/PIRL @version 1.15 */ public class View_Locator { private static final String ID = "PIRL.Viewers.View_Locator (1.15 2012/04/16 06:22:59)"; // Orientation: /** Specifies horizontal positioning of a view relative to the left side of the base. */ public static final int LEFT = 1 << 0; /** Specifies horizontal positioning of a view relative to the right side of the base. */ public static final int RIGHT = 1 << 1; /** Selects the base side orientation policy. */ public static final int SIDE = LEFT | RIGHT; /** Specifies vertical positioning of a view relative to the top side of the base. */ public static final int TOP = LEFT; /** Specifies vertical positioning of a view relative to the bottom side of the base. */ public static final int BOTTOM = RIGHT; /** Specifies centered positioning of a view relative to the base. */ public static final int CENTER = LEFT | RIGHT; /** Specifies positioning of a view relative to the outside side of the base. */ public static final int OUTWARD = 1 << 2; /** Specifies positioning of a view relative to the inside side of the base. */ public static final int INWARD = 0; /** Selects the direction relative to the base orientation policy. */ public static final int DIRECTION = OUTWARD; /** The default orientation: x: LEFT | INWARD, y: TOP | INWARD. */ public static Point Default_Orientation = new Point (LEFT | INWARD, TOP | INWARD); private Point Orientation = new Point (Default_Orientation); // Offsets: /** The default horizontal and vertical offsets relative to a base to apply to a view when it is relocated: 20x, 20y. */ public static Point Default_Offsets = new Point (20, 20); private Point Offsets = new Point (Default_Offsets); // Frame Margins: /** The default top, left, bottom and right margins to be used with the base dimensions when calculating the relocation position of a view: 0, 0, 0, 0. */ public static Insets Default_Frame_Margins = new Insets (0, 0, 0, 0); private Insets Frame_Margins = Default_Frame_Margins; // Display Bounds: /** The effective display bounds: [left, right), [top, bottom).

    Unless the user specifies the {@link #Display(Rectangle) Display} it is set to the {@link #Screen_Bounds(Rectangle) Screen_Bounds} as soon as the latter becomes valid. */ private Insets Display_Bounds = null; /** The bounds of the screen display. The first view encountered is used to obtain its GraphicsConfiguration bounds which is taken to be the bounds of the screen used for all subsequent views. This is usually sufficient. */ private static Insets Screen_Bounds = null; /** The default location to position a view if it would otherwise overlap a side of the display: 175x, 75y. */ public static Point Default_Warp_Location = new Point (175, 75); private Point Warp_Location = new Point (Default_Warp_Location); /** The default horizontal and/or vertical offsets to apply to the {@link #Warp_Location() Warp_Location} when it is moved along the corresponding axis. */ public static Point Default_Warp_Offsets = new Point (130, 20); private Point Warp_Offsets = new Point (Default_Warp_Offsets); // The direction to move the warp location. private static int HORIZONTAL = 1 << 0, VERTICAL = 1 << 1; private static Toolkit TOOLKIT = Toolkit.getDefaultToolkit (); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_LOCATORS = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a View_Locator using another View_Locator for {@link #Policies(View_Locator) Policies}, and positions the view to the {@link #Warp_Location() Warp_Location}.

    @param view A Window to position (may be null). @param locator A View_Locator used to set the Policies of the new View_Locator. If null then default values will be used. */ public View_Locator ( Window view, View_Locator locator ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> View_Locator"); Policies (locator); if (view != null) { Display (view); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" View_Locator:" + NL + " Set view location to the Warp_Location - " + Warp_Location); view.setLocation (Warp_Location); move_warp_location (HORIZONTAL); } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< View_Locator"); } /** Constructs a View_Locator using default policies, and positions the view to the {@link #Warp_Location() Warp_Location}.

    @param view The Window to be positioned. */ public View_Locator ( Window view ) {this (view, null);} /** Constructs a View_Locator as a copy of another View_Locator.

    @param locator The View_Locator to copy. */ public View_Locator ( View_Locator locator ) {this (null, locator);} /** Constructs a View_Locator with the specified orientation policies.

    @param horizontal_orientation The horizontal {@link #Orientation(int, int) Orientation} policies. @param vertical_orientation The vertical {@link #Orientation(int, int) Orientation} policies. */ public View_Locator ( int horizontal_orientation, int vertical_orientation ) { this (); Orientation (horizontal_orientation, vertical_orientation); } /** Constructs a View_Locator using default policies. */ public View_Locator () {this (null, null);} /*============================================================================== Accessors */ /** Sets the view relocation polices for this View_Locator from the policies of another View_Locator.

    @param policies The View_Locator from which to copy all relocation parameters. If null, defaults will be applied. @return This View_Locator. */ public View_Locator Policies ( View_Locator policies ) { if (policies == null) { Orientation = Default_Orientation; Offsets = Default_Offsets; Warp_Location = Default_Warp_Location; Warp_Offsets = Default_Warp_Offsets; Frame_Margins = Default_Frame_Margins; Display (Screen_Bounds); } else { Orientation = new Point (policies.Orientation); Offsets = new Point (policies.Offsets); Warp_Location = new Point (policies.Warp_Location); Warp_Offsets = new Point (policies.Warp_Offsets); Frame_Margins = new Insets ( policies.Frame_Margins.top, policies.Frame_Margins.left, policies.Frame_Margins.bottom, policies.Frame_Margins.right ); if (policies.Display_Bounds == null) Display_Bounds = null; else Display (policies.Display_Bounds); } return this; } /** Sets the boundary to use when views are relocated.

    The boundary does not need to be coincident with the real screen boundaries. For example, if it is desirable to allow a view to be relocated such that it would overlap the screen display, use a Rectangle that is positioned and/or sized beyond the screen bounds by the maximum amount of overlap to be allowed.

    @param rectangle The Rectangle specifying the bounds of the effective display. If null, then the display boundary will be reset to the boundary of the screen. @return This View_Locator. */ public View_Locator Display ( Rectangle rectangle ) { if (rectangle == null) { if (Screen_Bounds == null) Display_Bounds = null; else Display (Screen_Bounds); } else Display_Bounds = new Insets ( rectangle.y, rectangle.x, rectangle.y + Math.abs (rectangle.height), rectangle.x + Math.abs (rectangle.width) ); return this; } /** Initialize the {@link #Screen() screen bounds} and {@link #Display(Rectangle) display bounds} from a view.

    If the Screen_Bounds was already set it is not reset.

    If the view is null or not showing on the screen the screen size is obtained from the AWT Toolkit. Otherwise the bounds of the view's GraphicsConfiguration is used.

    If the Display_Bounds has not yet been set it is set to the Screen_Bounds.

    @param view A Window view reference. */ private void Display ( Window view ) { if (Screen_Bounds == null) { Rectangle screen_rectangle; if (view == null || ! view.isShowing ()) screen_rectangle = new Rectangle (TOOLKIT.getScreenSize ()); else screen_rectangle = view.getGraphicsConfiguration ().getBounds (); Screen_Bounds = new Insets ( screen_rectangle.y, screen_rectangle.x, screen_rectangle.y + screen_rectangle.height, screen_rectangle.x + screen_rectangle.width ); } if (Display_Bounds == null) Display (Screen_Bounds); } /** Calls {@link #Display(Rectangle) Display} with a Rectangle constructed from the Inset values.

    @param display_bounds The Insets values used to construct a Rectangle: x = left, y = top, width = right - left, height = bottom - top. */ private void Display ( Insets display_bounds ) { if (display_bounds != null) Display (new Rectangle ( display_bounds.left, display_bounds.top, display_bounds.right - display_bounds.left, display_bounds.bottom - display_bounds.top )); } /** Gets the Rectangle for the current display bounds.

    @return A Rectangle describing the current display bounds. Note: This will be null if the display bounds have not yet been determined. */ public Rectangle Display () { if (Display_Bounds == null) return null; return new Rectangle ( Display_Bounds.left, Display_Bounds.top, Display_Bounds.right - Display_Bounds.left, Display_Bounds.bottom - Display_Bounds.top ); } /** Sets the screen boundary to use for all View_Locators.

    Normally this is automatically found by using GraphicsConfiguration of the first view encountered.

    The boundary does not need to be coincident with the real screen boundaries. For example, if it is desirable to allow a view to be relocated such that it would overlap the screen display, use a Rectangle that is positioned and/or sized beyond the screen bounds by the maximum amount of overlap to be allowed.

    @param rectangle The Rectangle specifying the bounds of the effective screen. If null, then the screen boundary will be reset using the GraphicsConfiguration of the next view encountered. @return This View_Locator. */ public View_Locator Screen ( Rectangle rectangle ) { if (rectangle == null) Screen_Bounds = null; else Screen_Bounds = new Insets ( rectangle.y, rectangle.x, rectangle.y + Math.abs (rectangle.height), rectangle.x + Math.abs (rectangle.width) ); return this; } /** Gets the Rectangle for the current effective screen bounds.

    @return A Rectangle describing the current effective screen bounds. Note: This will be null if the screen bounds have not yet been determined. */ public Rectangle Screen () { if (Screen_Bounds == null) return null; return new Rectangle ( Screen_Bounds.left, Screen_Bounds.top, Screen_Bounds.right - Screen_Bounds.left, Screen_Bounds.bottom - Screen_Bounds.top ); } /** Sets the orientation policies when relocating a view relative to a base.

    The values used may be a combination, by ORing, of:

    • x (horizontal)
      LEFT, RIGHT or CENTER
      Locate the view relative to the left or right side or center of the base.
      OUTWARD or INWARD
      Locate the view towards the inside or ourside of the base.
    • y (vertical)
      TOP, BOTTOM orCENTER
      Locate the view relative to the top, bottom or center of the base.
      OUTWARD or INWARD
      Locate the view towards the inside or ourside of the base.

    @param orientation The orientation policies value. @return This View_Locator. */ public View_Locator Orientation (Point orientation) { if (orientation == null) Orientation = Default_Orientation; else Orientation = new Point (orientation); return this; } /** Sets the orientation policies when relocating a view relative to a base.

    @param x The horizontal relocation policies. @param y The vertical relocation policies. @see #Orientation(Point) */ public View_Locator Orientation (int x, int y) { Orientation = new Point (x, y); return this; } /** Gets the orientation polices.

    The values may be examined by ANDing with the SIDE and comparing with the LEFT/RIGHT/CENTER or TOP/BOTTOM/CENTER constants, and/or ANDing with the DIRECTION and comparing with the OUTWARD/INWARD constants.

    @return The orientation policies as a Point of x and y values. */ public Point Orientation () {return new Point (Orientation);} /** Sets the horizontal orientation policies.

    @param policies A horizontal orientation value. @return This View_Locator. @see #Orientation(Point) */ public View_Locator Horizontal (int policies) { Orientation.x = policies; return this; } /** Gets the horizontal orientation policies.

    @return A horizontal orientation value. @see #Orientation(Point) */ public int Horizontal () {return Orientation.x;} /** Sets the vertical orientation policies.

    @param policies Vertical orientation policies. @return This View_Locator. @see #Orientation(Point) */ public View_Locator Vertical (int policies) { Orientation.y = policies; return this; } /** Gets the vertical orientation policies.

    @return Either TOP or BOTTOM or-ed with either INWARD or OUTWARD. @see #Orientation(Point) */ public int Vertical () {return Orientation.y;} /** Sets the location on the screen to which to "warp" a view if relocating it would cause it to overlap the display bounds.

    @param location The setLocation Point to use if a view is warped. @return This View_Locator. */ public View_Locator Warp_Location (Point location) { if (location == null) Warp_Location = new Point (Default_Warp_Location); else Warp_Location = new Point (location); return this; } /** Sets the location on the screen to which to "warp" a view if relocating it would cause it to overlap the display bounds.

    @param x The horizontal setLocation Point to use if a view is warped. @param y The vertical setLocation Point to use if a view is warped. @return This View_Locator. @see java.awt.Component#setLocation(Point) */ public View_Locator Warp_Location (int x, int y) { Warp_Location = new Point (x, y); return this; } /** Gets the current location to which to "warp" a view if relocating it would cause it to overlap the display bounds.

    @return The Point for the current warp location. */ public Point Warp_Location () {return new Point (Warp_Location);} /** Sets the horizontal and vertical offsets to be applied when a view is relocated.

    @param offsets The Point containing the horizontal (x) and vertical (y) offsets. @return This View_Locator. @see #Offsets(int, int) */ public View_Locator Offsets (Point offsets) { if (offsets == null) Offsets = new Point (Default_Offsets); else Offsets = new Point (offsets); return this; } /** Sets the horizontal and vertical offsets to be applied when a view is relocated.

    @param x The horizontal offset amount. @param y The vertical offset amount. @return This View_Locator. @see #Offsets(Point) */ public View_Locator Offsets (int x, int y) { Offsets = new Point (x, y); return this; } /** Gets the horizontal and vertical offsets to be applied when a view is relocated.

    @return The Point containing the horizontal (x) and vertical (y) offsets. */ public Point Offsets () {return new Point (Offsets);} /** Sets the horizontal and vertical offsets to be applied when the warp location is automatically moved.

    @param offsets The Point containing the horizontal (x) and vertical (y) offsets. @return This View_Locator. */ public View_Locator Warp_Offsets (Point offsets) { if (offsets == null) Warp_Offsets = new Point (Default_Warp_Offsets); else Warp_Offsets = new Point (offsets); return this; } /** Sets the horizontal and vertical offsets to be applied when the warp location is automatically moved.

    @param x The horizontal offset amount. @param y The vertical offset amount. @return This View_Locator. */ public View_Locator Warp_Offsets (int x, int y) { Warp_Offsets = new Point (x, y); return this; } /** Gets the horizontal and vertical offsets to be applied when the warp location is automatically moved.

    @return The Point containing the horizontal (x) and vertical (y) offsets. */ public Point Warp_Offsets () {return new Point (Warp_Offsets);} /** Sets the margins around a view frame to be used when calculating a view relocation position.

    @param margins Insets providing the top, left, bottom, and right margins. @return This View_Locator. */ public View_Locator Frame_Margins (Insets margins) { if (margins == null) Frame_Margins = Default_Frame_Margins; else Frame_Margins = new Insets (margins.top, margins.left, margins.bottom, margins.right); return this; } /** Sets the margins around a view frame to be used when calculating a view relocation position.

    @param top The margin for the top side of a frame. @param left The margin for the left side of a frame. @param bottom The margin for the bottom side of a frame. @param right The margin for the right side of a frame. @return This View_Locator. */ public View_Locator Frame_Margins (int top, int left, int bottom, int right) { Frame_Margins = new Insets (top, left, bottom, right); return this; } /** Gets the margins to be use for a view frame.

    @return Insets providing the top, left, bottom, and right margins. */ public Insets Frame_Margins () { return new Insets (Frame_Margins.top, Frame_Margins.left, Frame_Margins.bottom, Frame_Margins.right); } /** Provides a description of the policies and parmeters of this View_Locator.

    @return A descriptive String (not new-line terminated). */ public String toString () { String string = ID + NL + "Horizontal Orientation: "; if ((Orientation.x & SIDE) == CENTER) string += "Centered" + NL; else string += (((Orientation.x & SIDE) == LEFT) ? "Left" : "Right") + ' ' + (((Orientation.x & DIRECTION) == OUTWARD) ? "Outward" : "Inward") + NL; string += " Vertical Orientation: "; if ((Orientation.y & SIDE) == CENTER) string += "Centered" + NL; else string += (((Orientation.y & SIDE) == TOP) ? "Top" : "Bottom") + ' ' + (((Orientation.y & DIRECTION) == OUTWARD) ? "Outward" : "Inward") + NL; string += " Offsets = " + coordinates (Offsets) + NL + " Frame_Margins = " + coordinates (Frame_Margins) + NL + " Screen_Bounds = " + coordinates (Screen_Bounds) + NL + "Display_Bounds = " + coordinates (Display_Bounds) + NL + " Warp_Location = " + coordinates (Warp_Location) + NL + " Warp_Offsets = " + coordinates (Warp_Offsets); return string; } /* The coordinates methods remove the leading portion of a String before the first '[' character. The leading portion contains the class name. Only the coordinate value listing is desired. */ private String coordinates (Point location) { if (location == null) return "null"; String description = location.toString (); return description.substring (description.indexOf ('[')); } private String coordinates (Rectangle location) { if (location == null) return "null"; String description = location.toString (); return description.substring (description.indexOf ('[')); } private String coordinates (Insets location) { if (location == null) return "null"; String description = location.toString (); return description.substring (description.indexOf ('[')); } /*============================================================================== Locators */ /** Relocates a view relative to a base view.

    @param view The Window to be relocated on the screen. @param base The Window to use as the base bounds reference. @see #Orientation(Point) @see #Offsets(Point) @see #Display(Rectangle) @see #Warp_Location(Point) @see #Warp_Offsets(Point) @see #Frame_Margins(Insets) @see java.awt.Component#setLocation(Point) @see #Relocate(Window, Rectangle) */ public void Relocate ( Window view, Window base ) { // Ensure that the screen and display boundaries are set. Display (base); Relocate (view, base.getBounds ()); } /** Relocates a view relative to base bounds.

    The view location coordinates are adjusted. The horizontal location is changed according to the LEFT, RIGHT or CENTER {@link #Horizontal(int) horizontal positioning policies} and the vertical location is changed according to the TOP, BOTTOM or CENTER {@link #Vertical(int) vertical positioning policies}.

    Centering policies set the view location such that the center of the view is aligned with the center of the base. N.B.: Centering polices ignore the direction of the orientation and do not use {@link #Offsets(Point) offset} values.

    Other policies offset the view location either INWARD or OUTWARD relative to the LEFT or RIGHT and/or TOP or BOTTOM sides of the base location coordinates. OUTWARD motion positions the view outside of the base adjacent to the specified side, plus that side's frame margin, aligned with the top (for horizonatal motion) or left (for vertical motion) side of the base before applying the offsets. INWARD motion positions the view towards the inside of the base adjacent to the specified sides before applying the offsets. Note that the offset values may be positive, negative or zero.

    For example, to produce the effect of horizontal, top aligned window tiling for a View Window relative to a Base Window:

    
    	View_Locator locator = new View_Locator ();
    	locator
    		.Offsets (0, 0)
    		.Horizontal (View_Locator.RIGHT | View_Locator.OUTWARD)
    		.Vertical   (View_Locator.TOP   | View_Locator.INWARD);
    	locator.Relocate (View, Base);
    

    After the view location coordinates have been offset the view boundary is checked for having been repositioned across a {@link #Display(Rectangle) Display} boundary. If it has, then it is "warped" to the current {@link #Warp_Location(Point) Warp_Location}. This position is intended to keep the view from overlapping a boundary. However, if the view is larger than the display bounds then it is placed against the left and/or top display boundary according to which dimensions are too large to fit. Each time a view is warped the current Warp_Location is moved horizontally by the {@link #Warp_Offsets(Point) Warp_Offsets.y} amount. If this new position crosses a display boundary it is wrapped around to the other boundary and then the vertical position is also offset. When the vertical position crosses a boundary it is wrapped around to the other boundary. The movement of the Warp_Location is done to help prevent warped views from being positioned directly on top of another warped view.

    Once the view relocation position has been determined the view is moved to that location on the screen.

    @param view The Window to be relocated on the screen. @param base_bounds The Rectangle to use as a relative reference. @see #Orientation(Point) @see #Offsets(Point) @see #Display(Rectangle) @see #Warp_Location(Point) @see #Warp_Offsets(Point) @see #Frame_Margins(Insets) @see java.awt.Component#setLocation(Point) */ public void Relocate ( Window view, Rectangle base_bounds ) { // Ensure that the screen and display boundaries are set. Display (view); Rectangle view_bounds = view.getBounds (); Relocate (view_bounds, base_bounds); view.setLocation (view_bounds.x, view_bounds.y); } /** Relocates a view relative to a base Point.

    @param view The Window to be relocated on the screen. @param location The Point to use as a relative reference. This will be used as a Rectangle with no height or width. @see #Relocate(Window, Rectangle) */ public void Relocate ( Window view, Point location ) {Relocate (view, new Rectangle (location));} private void Relocate ( Rectangle view_bounds, Rectangle base_bounds ) { if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (">>> View_Locator.Relocate:" + NL +" view bounds = " + coordinates (view_bounds) + NL +" base bounds = " + coordinates (base_bounds)); // Horizontal positioning: view_bounds.x = base_bounds.x; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Horizontal orientation = " + Orientation.x); if ((Orientation.x & SIDE) == CENTER) { // Relative to the center of the base. view_bounds.x += (base_bounds.width >> 1) - (view_bounds.width >> 1); if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move relative to base center " + (base_bounds.x + (base_bounds.width >> 1)) + NL +" to " + view_bounds.x); } else if ((Orientation.x & SIDE) == LEFT) { // Relative to the left side of the base. if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move from base left side " + base_bounds.x); if ((Orientation.x & DIRECTION) == INWARD) { // Move towards the inside of the base. view_bounds.x += Offsets.x; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" inward by offset " + Offsets.x + NL +" to " + view_bounds.x); } else { // Move away from the outside of the base. view_bounds.x -= view_bounds.width + Offsets.x + Frame_Margins.left; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" outward by view width " + view_bounds.width + NL +" + offset " + Offsets.x + NL +" + left frame margin " + Frame_Margins.left +" to " + view_bounds.x); } } else { // Relative to the right side of the base. view_bounds.x += base_bounds.width; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move from base right side " + view_bounds.x); if ((Orientation.x & DIRECTION) == INWARD) { // Move towards the inside of the base. view_bounds.x -= view_bounds.width + Offsets.x + Frame_Margins.left; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" inward by view width " + view_bounds.width + NL +" + offset " + Offsets.x + NL +" + left frame margin " + Frame_Margins.left + NL +" to " + view_bounds.x); } else { // Move away from the outside of the base. view_bounds.x += Offsets.x + Frame_Margins.right; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" outward by offset " + Offsets.x + NL +" + right frame margin " + Frame_Margins.right + NL +" to " + view_bounds.x); } } // Vertical positioning: view_bounds.y = base_bounds.y; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Vertical orientation = " + Orientation.y); if ((Orientation.x & SIDE) == CENTER) { // Relative to the center of the base. view_bounds.y += (base_bounds.height >> 1) - (view_bounds.height >> 1); if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move relative to base center " + (base_bounds.y + (base_bounds.height >> 1)) + NL +" to " + view_bounds.y); } else if ((Orientation.y & SIDE) == TOP) { // Relative to the top of the base. if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move from base top side " + base_bounds.y); if ((Orientation.y & DIRECTION) == INWARD) { // Move towards the inside of the base. view_bounds.y += Offsets.y; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" inward by offset " + Offsets.y + NL +" to " + view_bounds.y); } else { // Move away from the outside of the base. view_bounds.y -= view_bounds.height + Offsets.y + Frame_Margins.top; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" outward by view height " + view_bounds.height + NL +" + offset " + Offsets.y + NL +" + top frame margin " + Frame_Margins.top + NL +" to " + view_bounds.y); } } else { // Relative to the bottom of the base. view_bounds.y += base_bounds.height; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Move from base bottom side " + view_bounds.y); if ((Orientation.y & DIRECTION) == INWARD) { // Move towards the inside of the base. view_bounds.y -= view_bounds.height + Offsets.y + Frame_Margins.top; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" inward by view height " + view_bounds.height + NL +" + offset " + Offsets.y + NL +" + top frame margin " + Frame_Margins.top + NL +" to " + view_bounds.y); } else { // Move away from the outside of the base. view_bounds.y += Offsets.y + Frame_Margins.bottom; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" outward by offset " + Offsets.y + NL +" + frame margin " + Frame_Margins.bottom + NL +" to " + view_bounds.y); } } // Warp the relocated view around the Display_Bounds. warp (view_bounds); if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println ("<<< View_Locator.Relocate:" + NL +" view bounds = " + coordinates (view_bounds)); } /** If the location for the view_bounds results in an overlap with the Display_Bounds, move it to the Warp_Location. Then move the Warp_Location. */ private void warp ( Rectangle view_bounds ) { if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (">>> View_Locator.warp:" + NL +" view bounds = " + coordinates (view_bounds) + NL +" Display_Bounds = " + coordinates (Display_Bounds)); int limit_x = Display_Bounds.right - view_bounds.width, limit_y = Display_Bounds.bottom - view_bounds.height; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" limits = " + limit_x + "x, " + limit_y + 'y'); if (view_bounds.x > limit_x || view_bounds.x < Display_Bounds.left || view_bounds.y > limit_y || view_bounds.y < Display_Bounds.top) { if (limit_x <= Display_Bounds.left) { limit_x = Display_Bounds.left; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Oversized view pushed against left side."); } else { while (Warp_Location.x > limit_x) move_warp_location (HORIZONTAL); limit_x = Warp_Location.x; } if (limit_y <= Display_Bounds.top) { limit_y = Display_Bounds.top; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Oversized view pushed against top side."); } else { while (Warp_Location.y > limit_y) move_warp_location (VERTICAL); limit_y = Warp_Location.y; } // Set the view to the warp location. view_bounds.x = limit_x; view_bounds.y = limit_y; // Offset the next warp location horizontally move_warp_location (HORIZONTAL); if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println ("<<< View_Locator.warp:" + NL +" view bounds = " + coordinates (view_bounds)); } } private void move_warp_location ( int direction ) { if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (">>> View_Locator.move_warp_location: " + direction + " -" + (((direction & HORIZONTAL) != 0) ? " HORIZONTAL" : "") + (((direction & VERTICAL) != 0) ? " VERTICAL" : "") + NL +" Display_Bounds = " + coordinates (Display_Bounds) + NL +" Warp_Location = " + coordinates (Warp_Location) + NL +" Warp_Offsets = " + coordinates (Warp_Offsets)); Horizontal: { if ((direction & HORIZONTAL) != 0) { Warp_Location.x += Warp_Offsets.x; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Horizontal Offset " + Warp_Offsets.x + NL +" to " + Warp_Location.x); if (Warp_Location.x >= Display_Bounds.right) { // Warp from the right to the left side of the screen. Warp_Location.x = Display_Bounds.left; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Warp through right side " + Display_Bounds.right + NL +" to left side " + Display_Bounds.left); // and move vertically. direction |= VERTICAL; } else if (Warp_Location.x < Display_Bounds.left) { // Warp from the left to the right side of the screen. Warp_Location.x = Display_Bounds.right - 1; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Warp through left side " + Display_Bounds.left + NL +" to right side " + (Display_Bounds.right - 1)); // and move vertically. direction |= VERTICAL; } } if ((direction & VERTICAL) != 0) { Warp_Location.y += Warp_Offsets.y; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Vertical Offset " + Warp_Offsets.y + NL +" to " + Warp_Location.y); if (Warp_Location.y >= Display_Bounds.bottom) { // Warp from the bottom to the top of the screen. Warp_Location.y = Display_Bounds.top; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Warp through bottom side " + Display_Bounds.bottom + NL +" to top side " + Display_Bounds.top); if ((direction & HORIZONTAL) == 0) { // and move horizontally. direction |= HORIZONTAL; break Horizontal; } } else if (Warp_Location.y < Display_Bounds.top) { // Warp from the top to the bottom of the screen. Warp_Location.y = Display_Bounds.bottom - 1; if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println (" Warp through top side " + Display_Bounds.top + NL +" to bottom side " + (Display_Bounds.bottom - 1)); if ((direction & HORIZONTAL) == 0) { // and move horizontally. direction |= HORIZONTAL; break Horizontal; } } } } if ((DEBUG & DEBUG_LOCATORS) != 0) System.out.println ("<<< View_Locator.move_warp_location: " + coordinates (Warp_Location)); } /*============================================================================== Look-and_Feel */ /** The default Look-and-Feel for {@link #Select_LAF(String)}. */ public static final String DEFAULT_LAF = "Java"; /** Select a GUI Look-and_Feel.

    A set of well-known LAF styles may be selected by their short names: Java, which is also known as Metal, Basic or Cross-platform; Motif; GTK; Synth; Windows and Windows_Classic. A fully qualified classpath for a {@link javax.swing.LookAndFeel} may be specified and an attempt will be made to load it into the {@link UIManager#setLookAndFeel(String) User Interface Manager}. If the name Native is specified the LAF associated with the host system will be selected. If the selected LAF can not be used by the User Interface Manager the Native LAF will be used instead.

    New windows ({@link JFrame} and {@link JDialog}) may be requested to use the selected LAF, if possible. However, this hint will only be effective if applied before the new windows are constructed.

    @param LAF_name The name of the selected LAF. This may be one of the well known short names or a fully qualified classpath for a LookAndFeel class. If null or the empty String the {@link #DEFAULT_LAF} will be selected. @param apply_to_window_frames If true the JFrame and JDialog classes will have a hint applied that they should use the selected LAF when a new object is constructed. @return An error message String. This will be null if the selected LAF was accepted by the User Interface Manager. Otherwise it will describe the reason that the selected LAF could not be used. */ public static String Select_LAF ( String LAF_name, boolean apply_to_window_frames ) { String error_message = null; if (LAF_name == null || LAF_name.length () == 0) LAF_name = DEFAULT_LAF; if (LAF_name.indexOf ('.') < 0) { String name = LAF_name.toUpperCase (); switch (name.charAt (0)) { case 'M': if (name.length () > 1 && name.charAt (1) == 'O') { // Motif LAF_name = "com.sun.java.swing.plaf.motif.MotifLookAndFeel"; break; } // Fall through to Metal. case 'J': // Java/Metal LAF_name = "javax.swing.plaf.metal.MetalLookAndFeel"; break; case 'B': // Basic LAF_name = "javax.swing.plaf.basic.BasicLookAndFeel"; break; case 'G': // GTK LAF_name = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; break; case 'S': // Synth LAF_name = "javax.swing.plaf.synth.SynthLookAndFeel"; break; case 'X': // XAWT LAF_name = "sun.awt.X11.XAWTLookAndFeel"; break; case 'W': // Windows if (name.indexOf ('C') > 0) { // Windows Classic LAF_name = "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel"; break; } LAF_name = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; break; case 'C': // Cross-platform try {UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ());} catch (Exception exception) {error_message = exception.getMessage ();} if (apply_to_window_frames) { JFrame.setDefaultLookAndFeelDecorated (true); JDialog.setDefaultLookAndFeelDecorated (true); } return error_message; default: return "Unknown LAF: " + LAF_name; case 'N': // Native return null; } } try {UIManager.setLookAndFeel (LAF_name);} catch (Exception exception) {error_message = exception.getMessage ();} if (apply_to_window_frames) { JFrame.setDefaultLookAndFeelDecorated (true); JDialog.setDefaultLookAndFeelDecorated (true); } return error_message; } /** Select a GUI Look-and_Feel.

    This is the same as using {@link #Select_LAF(String, boolean)} with the apply_to_window_frames argument set to true.

    @param LAF_name The name of the selected LAF. This may be one of the well known short names or a fully qualified classpath for a LookAndFeel class. If null or the empty String the {@link #DEFAULT_LAF} will be selected. @return An error message String. This will be null if the selected LAF was accepted by the User Interface Manager. Otherwise it will describe the reason that the selected LAF could not be used. */ public static String Select_LAF ( String LAF_name ) {return Select_LAF (LAF_name, true);} } // End of View_Locator class. pirl-2.3.8/PIRL/Viewers/Percent_Bar_UI.java0000644000175000017500000001151211742735303020167 0ustar mathieumathieu/* Percent_Bar_UI PIRL CVS ID: Percent_Bar_UI.java,v 1.5 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import java.awt.Color; import java.awt.Insets; import java.awt.Graphics; import java.awt.geom.Rectangle2D; /** A Percent_Bar_UI provides a ComponentUI delegate for the Percent_Bar component.

    @author Bradford Castalia, UA/PIRL @version 1.5 @see Percent_Bar */ public class Percent_Bar_UI extends Percent_BarUI { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Percent_Bar_UI (1.5 2012/04/16 06:22:59)"; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_PAINT = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Percent_Bar_UI object. */ public Percent_Bar_UI () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Percent_Bar_UI.Constructor"); } /** Construct a Percent_Bar_UI object for the specified JComponent.

    @param component The JComponent for which ComponentUI delegation is to be provided. @return A Percent_Bar_UI object. */ public static ComponentUI createUI ( JComponent component ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Percent_Bar_UI.createUI"); return new Percent_Bar_UI (); } /*============================================================================== Component Painting */ /** Paint the component on the graphics display device.

    @param graphics The Graphics on which to paint the component. @param component The JComponent to be painted. */ public void paint ( Graphics graphics, JComponent component ) { if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (">>> Percent_Bar_UI.paint"); Percent_Bar bar = (Percent_Bar)component; // Canvas. Insets insets = component.getInsets (); int canvas_width = bar.getWidth () - insets.left - insets.right, canvas_height = bar.getHeight () - insets.top - insets.bottom; if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (" canvas_width = " + canvas_width + '\n' +" canvas_height = " + canvas_height); // Set 0,0 at the upper-left corner of the bar. graphics.translate (insets.left, insets.top); // Bar. int x, y, width, height, orientation = bar.Orientation (); if ((orientation & 1) == 0) { // Horizontal orientation. width = (int)((bar.Percent () / 100.0) * canvas_width); height = canvas_height; y = 0; if (orientation == Percent_Bar.LEFT) x = 0; else x = canvas_width - width; } else { // Vertical orientation. height = (int)((bar.Percent () / 100.0) * canvas_height); width = canvas_width; x = 0; if (orientation == Percent_Bar.TOP) y = 0; else y = canvas_height - height; } if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (" orientation = " + orientation + '\n' +" percent = " + bar.Percent () + '\n' +" x = " + x + '\n' +" y = " + y + '\n' +" width = " + width + '\n' +" height = " + height + '\n' +" fore color = " + bar.getForeground () + '\n' +" back color = " + bar.getBackground () + '\n' +" opaque = " + bar.isOpaque ()); graphics.setColor (bar.getForeground ()); graphics.fillRect (x, y, width, height); if (bar.isOpaque ()) { // Background. if ((orientation & 1) == 0) { if (x == 0) x = width; else x = 0; width = canvas_width - width; } else { if (y == 0) y = height; else y = 0; height = canvas_height - height; } graphics.setColor (bar.getBackground ()); graphics.fillRect (x, y, width, height); } // Restore the graphics origin. graphics.translate (-insets.left, -insets.top); if ((DEBUG & DEBUG_PAINT) != 0) System.out.println ("<<< Percent_Bar_UI.paint"); } } // End of Percent_Bar_UI class. pirl-2.3.8/PIRL/Viewers/Parameter_Viewer0000755000175000017500000000077711115133462017735 0ustar mathieumathieu#!/usr/bin/perl # # A wrapper for the Java Parameter_View application class. # # CVS ID: Parameter_Viewer,v 1.1 2008/12/02 04:18:58 castalia Exp $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $CLASSPATH = $ENV{"CLASSPATH"}; if ($PIRL_JAVA_HOME) { if ($CLASSPATH) { $CLASSPATH = "$PIRL_JAVA_HOME:$CLASSPATH"; } else { $CLASSPATH = $PIRL_JAVA_HOME; } } @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Viewers.Parameter_View", @ARGV; exec @Arguments; pirl-2.3.8/PIRL/Viewers/Memory_History.java0000644000175000017500000003440411742735303020404 0ustar mathieumathieu/* Memory_History PIRL CVS ID: Memory_History.java,v 1.5 2012/04/16 06:22:59 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.Timer; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Vector; import java.util.Iterator; /** A Memory_History provides a history of Java Runtime Environment (JRE) memory use.

    The JRE reports three categories of memory: the total amount of {@link #Available_Memory() available memory} in the JRE heap space, the amount of {@link #Allocated_Memory allocated memory} used by objects, and the amount of {@link #Free_Memory() free memory} that is no longer in use by objects which can be garbage collected. */ public class Memory_History { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Memory_Chart (1.5 2012/04/16 06:22:59)"; /** The */ public static final int DEFAULT_MAX_SAMPLES = 32; //! Memory amount samples. protected int Samples = 0; /** The {@link #Free_Memory() free memory} sampling history. */ protected long[] Free; /** The {@link #Allocated_Memory() allocated memory} sampling history. */ protected long[] Allocated; //! Polling manager. private Timer Poll_Timer = null; //! The previous non-zero sampling rate. private int Previous_Rate = 0; //! ChangeListener objects that have been registered. private Vector Change_Listeners = new Vector (); //! The Java virtual machine (JVM) runtime environment (JRE) object. private static final Runtime JRE = Runtime.getRuntime (); /*============================================================================== Constructors */ /** Constructs at Memory_History with specified max samples and sampling rate.

    @param max_samples The maximum number of JRE memory use samples to acquire. @param rate The rate, in seconds, between memory samples. */ public Memory_History ( int max_samples, int rate ) { if (max_samples <= 0) max_samples = DEFAULT_MAX_SAMPLES; Free = new long[max_samples]; Allocated = new long[max_samples]; Rate (rate); } /** Constructs an idle Memory_History with {@link #DEFAULT_MAX_SAMPLES} sample capacity.

    The Memory_History will have no history capacity and a memory sampling will be disabled. */ public Memory_History () {this (0, 0);} /*============================================================================== Convenience functions */ /** Get the amount of memory allocated by the Java runtime environment but not currently in use.

    Free memory is part of the {@link #Allocated_Memory() allocated} memory pool but is not currently in use. Garbage collection frees memory.

    @return The amount, in bytes, of allocated but free memory. @see Runtime#freeMemory() */ public static long Free_Memory () {return JRE.freeMemory ();} /** Get the amount of memory currently in use by the Java runtime environment.

    Allocated memory is taken from the total amount of memory made available to the Java runtime environment when the Java virtual machine is started. Memory that is allocated, but not {@link #Free_Memory() free} is currently in use by the virtual machine, including any user objects it is managing.

    @return The amount, in bytes, of allocated memory. @see #Available_Memory() @see Runtime#totalMemory() */ public static long Allocated_Memory () {return JRE.totalMemory ();} /** Get the total amount of memory available to the Java runtime environment.

    When the Java virtual machine is started some amount of the host system memory is reserved for use by the Java runtime environment. {@link #Allocated_Memory() allocated} memory is taken from available memory when needed. The default amount of available memory can be changed using the java -Xmx command line option.

    @return The total amount of memory, in bytes, available to the Java runtime environment. @see Runtime#maxMemory */ public static long Available_Memory () {return JRE.maxMemory ();} /*============================================================================== Sampling */ /** Get the rate at which memory usage is being sampled.

    @return The sampling rate in seconds. This will be zero if sampling is not active. @see #Rate(int) @see #Previous_Rate() */ public int Rate () { if (Poll_Timer == null || ! Poll_Timer.isRunning ()) return 0; return Poll_Timer.getDelay () / 1000; } /** Set the rate at which to sample memory usage.

    If the current sampling rate is the same as the new sampling rate, nothing is done.

    If the new sampling rate is less than or equal to zero the sampling timer is stopped.

    If the new sampling rate is positive a samping timer will be constructed if one has not already been provided; otherwise the current sampling history will be {@link #Clear() cleared}. If the sampling timer is not already running it will be started. The sampling timer's action event will {@link #Update() updated} the memory history at the specified rate, and will continue to be updated at the same rate until sampling is discontinued by setting the rate at or below zero. The sampling rate may be changed while sampling is active.

    The intention is to have a sample at each rate point in time. However, the timer can not guarantee this: Other activity on the system may prevent the timer from generating an event at the requested time. Nevertheless, by preventing events from being coalesced there will still be the correct number of events generated for the overall amount of elapsed time even though the burst of catch-up events will result in memory values not being sampled at the expected time; no sampling events will be dropped.

    After the sampling rate has been changed a ChangeEvent is {@link #fireChange() fired}.

    @param rate The rate, in seconds, between each memory usage sample. If less than or equal to zero sampling will be discontinued. @return The previous sampling rate. If this value is not zero the {@link #Previous_Rate() previous rate} will be recorded. */ public synchronized int Rate ( int rate ) { int previous_rate = Rate (); if (previous_rate == rate) return previous_rate; if (rate <= 0) { if (Poll_Timer != null) Poll_Timer.stop (); } else { rate *= 1000; if (Poll_Timer == null) { // First time setup. Poll_Timer = new Timer (rate, new ActionListener () {public void actionPerformed (ActionEvent event) {Update ();}}); Poll_Timer.setRepeats (true); Poll_Timer.setCoalesce (false); } else Clear (); if (Poll_Timer.getDelay () * 1000 != rate) Poll_Timer.setDelay (rate); if (! Poll_Timer.isRunning ()) Poll_Timer.start (); } if (previous_rate != 0) Previous_Rate = previous_rate; fireChange (); return previous_rate; } /** Get the previous sampling rate.

    The value returned is the previous non-zero sampling rate that was used. However, if sampling was never enabled then this will be zero.

    @return The previous non-zero sampling rate, or zero if sampling was never enabled. @see #Rate() */ public int Previous_Rate () {return Previous_Rate;} /** Update the memory history with new samples.

    After the memory use samples have been recorded a ChangeEvent is {@link #fireChange() fired}.

    @see #Historical(long, long) */ private void Update () { Historical (Free_Memory (), Allocated_Memory ()); fireChange (); } /** Add values to the memory history.

    If the history {@link #Max_Samples() capacity} has not been reached the valid {@link #Samples() samples count} is incremented. All the current allocated and free memory sample values are shifted down in their arrays and then the new values are set as the current (index zero) entries.

    It might be argued that new sample values should be added to the end of the history arrays rather than at the beginning. However, once the history capacity has been reached the array contents would need to be shifted anyway. It is conceptually and programatically easier to work with the history arrays if the most recent sample is first.

    @param free The new free memory sample value. @param allocated The new allocated memory sample value. @see #Allocated_History() @see #Free_History() */ protected synchronized void Historical ( long free, long allocated ) { int there = Samples; if (Samples == Allocated.length) --there; else ++Samples; for (int here = there - 1; there != 0; --here, --there) { Free[there] = Free[here]; Allocated[there] = Allocated[here]; } Free[0] = free; Allocated[0] = allocated; } /** Get the count of valid memory history samples.

    @return The number of valid memory history samples that are available. @see #Allocated_History() @see #Free_History() */ public int Samples () {return Samples;} /** Get the maximum number of history samples that can be recorded.

    @return The maximum number of history samples that will be recorded. @see #Allocated_History() @see #Free_History() */ public int Max_Samples () {return Allocated.length;} /** Set the maximum number of history samples that can be recorded.

    If the new sample capacity is different from the current sample capacity new sample history arrays are allocated and the previous valid sample values, up to the current {@link #Samples() sample count} or the new capacity (whichever is smaller), are copied into the new history arrays. Then the new history arrays are set as the current history arrays.

    After the history arrays have been changed a ChangeEvent is {@link #fireChange() fired}.

    @param max_samples The memory history sample capacity. If the value is less than or equal to zero the {@link #DEFAULT_MAX_SAMPLES} will be used. */ public synchronized void Max_Samples ( int max_samples ) { if (max_samples <= 0) max_samples = DEFAULT_MAX_SAMPLES; if (max_samples == Allocated.length) // No change. return; // Allocate the new history buffers. long[] free_history = new long[max_samples], allocated_history = new long[max_samples]; if (Samples > max_samples) // Drop the excess samples. Samples = max_samples; // Copy the samples from the old history to the new history. for (int index = 0; index < Samples; ++index) { free_history[index] = Free[index]; allocated_history[index] = Allocated[index]; } // Use the new histroy buffers. Free = free_history; Allocated = allocated_history; // Let everyone know that the history buffers have changed. fireChange (); } /** The memory history is emptied.

    After the memory history has been reset a ChangeEvent is {@link #fireChange() fired}.

    @see #Clear() */ public void Reset () { Clear (); fireChange (); } /** Clear the memory history.

    The memory sample history array values and the count of valid samples are set to zero.

    @see #Samples() @see #Allocated_History() @see #Free_History() */ protected synchronized void Clear () { while (Samples > 0) { Free[--Samples] = 0; Allocated[Samples] = 0; } } /** Get free memory samples history.

    @return An array of long values that contain the {@link #Historical(long, long) history} of {@link #Free_Memory() free memory} samples. This array will have as many entries as the {@link #Samples() samples count}. */ public synchronized long[] Free_History () { long[] history = new long[Samples]; for (int index = 0; index < Samples; ++index) history[index] = Free[index]; return history; } /** Get allocated memory samples history.

    @return An array of long values that contain the {@link #Historical(long, long) history} of {@link #Allocated_Memory() allocated memory} samples. This array will have as many entries as the {@link #Samples() samples count}. */ public synchronized long[] Allocated_History () { long[] history = new long[Samples]; for (int index = 0; index < Samples; ++index) history[index] = Allocated[index]; return history; } /** Get the memory samples history.

    @return An array of long value pairs that contain the {@link #Historical(long, long) history} of {@link #Allocated_Memory() allocated memory} samples in the first value of the pair (array[i][0] and {@link #Free_Memory() free memory} samples in the second value of the pair (array[i][1]). This array will have as many sample value pair entries as the {@link #Samples() samples count}. */ public synchronized long[][] History () { long[][] history = new long[Samples][2]; for (int index = 0; index < Samples; ++index) { history[index][0] = Allocated[index]; history[index][1] = Free[index]; } return history; } /** Register a ChangeListener.

    @param listener The ChangeListener to be added to the list registered for this Memory_History. The listener is not added if it is already registered.. */ public void addChangeListener ( ChangeListener listener ) { if (listener != null && ! Change_Listeners.contains (listener)) Change_Listeners.add (listener); } /** Remove a registered ChangeListener.

    @param listener The ChangeListener to be removed. */ public boolean removeChangeListener ( ChangeListener listener ) {return Change_Listeners.remove (listener);} /** Send a ChangeEvent referencing this Memory_History to each registered ChangeListener.

    @see #addChangeListener(ChangeListener) */ protected void fireChange () { Iterator listeners = Change_Listeners.iterator (); while (listeners.hasNext ()) ((ChangeListener)listeners.next ()).stateChanged (new ChangeEvent (this)); } } // End of Memory_History class. pirl-2.3.8/PIRL/Viewers/Value_View.java0000644000175000017500000001562011742735303017460 0ustar mathieumathieu/* Value_View PIRL CVS ID: Value_View.java,v 1.13 2012/04/16 06:22:59 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.PVL.*; import javax.swing.JTree; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.tree.*; import javax.swing.JScrollPane; import javax.swing.JFrame; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; /** A Value_View provides a view of an array Value. The display uses a JTree to represent the array of Values. Any Value in the array that is itself an array may be expanded to display its contents. Each Value is shown as the String provided by the Value's toString method.

    This Swing GUI can be employed whenever the display of a Value array is needed by an application. A main method is provided using this class as an application for viewing the array Values from a file containing PVL statements.

    @see Value @see JTree @author Bradford Castalia, UA/PIRL @version 1.13 */ public class Value_View extends JFrame { private static final String ID = "PIRL.Viewers.Value_View (1.13 2012/04/16 06:22:59)"; // Is this an application (started from main)? private static boolean _Application_ = false; // The initial screen location when used as an application. private static final Point DEFAULT_INITIAL_LOCATION = new Point (175, 75); // Total number of displays created by the application. private static int _Total_Displays_ = 0; private Parameter_Pane Enclosing_Parameter_Pane; /** The scroll pane containing the JTable. */ private Value_Pane The_Value_Pane; /*============================================================================== Constructors */ /** Creates a Value_View from the specified Value and gives it the specified title. This constructor should be used when this class is being used as a stand-alone application

    @param title The window title String. If null, then the default title "Value Array" will be used. @param value The Value to be displayed. @throws PVL_Exception If the Value is not an ARRAY type. The message is BAD_ARGUMENT. */ public Value_View ( String title, Value value ) throws PVL_Exception { this(title, value, (Parameter_Pane)null); } /** Creates a Value_View from the specified Value and gives it the specified title. This constructor should be used when this class is being used as part of a larger application

    @param title The window title String. If null, then the default title "Value Array" will be used. @param value The Value to be displayed. @param enclosing_parameter_pane The Parameter_Pane that created this value view. If no Parameter_Pane created this Value_View, set this to null or use the other constructor. @throws PVL_Exception If the Value is not an ARRAY type. The message is BAD_ARGUMENT. */ public Value_View ( String title, Value value, Parameter_Pane enclosing_parameter_pane ) throws PVL_Exception { if (title == null) title = "Value_View"; setTitle (title); The_Value_Pane = new Value_Pane (value, enclosing_parameter_pane); The_Value_Pane.setMinimumSize (new Dimension (50, 40)); int height = 25 * value.getChildCount (); if (height < 40) height = 40; if (height > 200) height = 200; The_Value_Pane.setPreferredSize (new Dimension (100, height)); // Add the tree view pane to this frame. getContentPane ().add (The_Value_Pane, BorderLayout.CENTER); // On window close... addWindowListener (new WindowAdapter () { public void windowClosing (WindowEvent event) { if (--_Total_Displays_ == 0 && _Application_) System.exit (0); } }); pack (); _Total_Displays_++; } /** Creates a Value_View from the specified Value.

    @param value The Value to be displayed. @throws PVL_Exception If the Value is not an ARRAY type. The message is BAD_ARGUMENT. */ public Value_View ( Value value ) throws PVL_Exception {this (null, value);} /*============================================================================== Accessors */ /** Gets the Value_Pane used by this Value_View.

    @return The Value_Pane used by this Value_View. */ public Value_Pane Value_Pane () {return The_Value_Pane;} /** Gets the JTree being used to represent the Values in the display.

    @return The JTreeTable representing this Parameter_View. */ public JTree Tree () {return The_Value_Pane.Tree ();} /*============================================================================== Application */ /** Creates a Value_View displaying each of the array Values found in the file listed on the command line. */ public static void main (String[] arguments) { if (arguments.length == 0) { System.err.println ("Usage: java Value_View "); System.exit (1); } _Application_ = true; try { Parameter parameters = new Parameter (arguments[0], new Parser (new File (arguments[0]))), parameter = null, test_parameter = new Parameter () .Classification (Parameter.ASSIGNMENT); /* .Value ( new Value () .Type (Value.SET) ); */ Selector criteria = new Selection () .Classification (true) .Specific (false); /* .Value (true) .Type (true) .And (true); */ Value_View view, previous_view = null; View_Locator locator = new View_Locator (); while ((parameter = parameters.Find (test_parameter, criteria, parameter)) != null) { view = new Value_View (parameter.Path_Name (), parameter.Value ()); // Set the new screen location. if (previous_view == null) view.setLocation (DEFAULT_INITIAL_LOCATION); else locator.Relocate (view, previous_view); view.setVisible (true); previous_view = view; } if (_Total_Displays_ == 0) { System.out.println ("No Array Value found."); System.exit (1); } } catch (Exception exception) { System.err.println (exception.getMessage ()); System.exit (-1); } } } pirl-2.3.8/PIRL/Viewers/Stream_Monitor.java0000644000175000017500000010404111742735303020350 0ustar mathieumathieu/* Stream_Monitor PIRL CVS ID: Stream_Monitor.java,v 1.17 2012/04/16 06:22:59 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.Utilities.Styled_Writer; import PIRL.Strings.String_Utilities; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.JScrollBar; import javax.swing.JTextPane; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.Caret; import javax.swing.text.DefaultCaret; import javax.swing.text.BadLocationException; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JFileChooser; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.MouseListener; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelListener; import java.awt.event.MouseWheelEvent; import java.awt.Color; import java.io.File; import java.io.Writer; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.OutputStreamWriter; import java.io.OutputStream; import java.io.IOException; import java.util.Hashtable; import java.util.Enumeration; /** A Stream_Monitor provides a scrolling display of styled text written to it.

    Text may be provided directly to the monitor; in this case it acts like the JTextPane it employs. Or text may be written to the stream the monitor offers. Adding text to the monitor is thread safe. Text is added to the bottom of the current content. The capacity of the monitor - the amount of text that will be retained - may be controlled. When the capacity has been reached text is removed from the top of the current content in whole line increments. The text content may be edited by the user, saved to a disk file, or cleared.

    The monitor JTextPane is contained within a JScrollPane initialized with the default scroll bar policies. The vertical scroll bar is actively managed such that when the thumb is located at the bottom of the scroll bar it will stay there, and text added to the monitor will scroll up keeping the end of the last text added visible. However, if the thumb is moved to any other location the thumb will remain there and the visible text will not move unless it must scroll up when the capacity is reached and excess text is removed from the top.

    Text written to the monitor may be auto-styled with a table of text-to-style associations. The text-to-style associations may be managed as desired.

    File and View menus configured for text file saving, capacity control and content clearing are provided for use in an application menu bar. These menus may be modified to suit the needs of the application.

    @author Bradford Castalia - UA/PIRL @version 1.17 */ public class Stream_Monitor extends JPanel implements Styled_Writer { /** Class name and identification. */ public static final String ID = "Stream_Monitor (1.17 2012/04/16 06:22:59)"; /** Text style attributes suitable for "stdout" {@link #Auto_Style(String AttributeSet) auto-styling}.

    The style is a bold Monospaced font. */ public static final SimpleAttributeSet stdout_STYLE = new SimpleAttributeSet (); /** Text style attributes suitable for "stderr" {@link #Auto_Style(String AttributeSet) auto-styling}.

    The style is a bold Monospaced font with a yellow background. */ public static final SimpleAttributeSet stderr_STYLE = new SimpleAttributeSet (); /** A text style that will be noticed.

    The style is bold red text. */ public static final SimpleAttributeSet NOTICE_STYLE = new SimpleAttributeSet (); /** A highlighted text style.

    The style is blue text. */ public static final SimpleAttributeSet HIGHLIGHT_STYLE = new SimpleAttributeSet (); /** A marker text style.

    The style is a bold Monospaced font. */ public static final SimpleAttributeSet MARKER_STYLE = new SimpleAttributeSet (); static { StyleConstants.setFontFamily (stdout_STYLE, "Monospaced"); StyleConstants.setBold (stdout_STYLE, true); StyleConstants.setFontFamily (stderr_STYLE, "Monospaced"); StyleConstants.setBold (stderr_STYLE, true); StyleConstants.setBackground (stderr_STYLE, Color.YELLOW); StyleConstants.setBold (NOTICE_STYLE, true); StyleConstants.setForeground (NOTICE_STYLE, Color.RED); StyleConstants.setForeground (HIGHLIGHT_STYLE, Color.BLUE); StyleConstants.setFontFamily (MARKER_STYLE, "Monospaced"); StyleConstants.setBold (MARKER_STYLE, true); } private boolean Auto_Style = false; private Hashtable Auto_Style_Table = new Hashtable (); private DefaultStyledDocument Monitor_Document; private JTextPane Monitor_Text_Pane; private JScrollPane Scroll_Pane; // Auto-scroll controls. private DefaultCaret Text_Caret = null; private JScrollBar Scroll_Bar = null; private int Default_Update_Policy; private static final int SCROLL_AT_BOTTOM_MARGIN = 10; /** The lower limit on the {@link #Capacity(int) capacity} value. */ public static final int MIN_CAPACITY = 8 * 1024; private static int Default_Capacity = 1024 * 1024; private volatile int Capacity = Default_Capacity; public static final int Stream_Buffer_Capacity = 1024; private Monitor_Writer Character_Writer = new Monitor_Writer (); private Monitor_OutputStream Byte_Stream = new Monitor_OutputStream (); private String Monitor_Text_Filename = null, CWD = System.getProperty ("user.dir"); private JMenu File_Menu, View_Menu; private static String NL = System.getProperty ("line.separator"); private static int NL_length = NL.length (); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_WRITE = 1 << 1, DEBUG_AUTO_SCROLL = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Stream_Monitor. */ public Stream_Monitor () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Stream_Monitor"); Menus (); Panels (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Stream_Monitor"); } /*============================================================================== Accessors */ /** Get an OutputStream for writing to the monitor display.

    The stream is line-oriented: Each byte written is buffered until an end-of-line sequence, or a maximum of is encountered at which point the buffer is {@link #Write(String) written} to the monitor display. An end-of-line line sequence is any one of a line feed ('\n', 0x0A), a carriage return ('\r', 0x0D), or a carriage return followed immediately by a linefeed.

    N.B.: Because both the OutputStream returned by this method and the {@link #Writer()} are buffered output to the same monitor display it is recommended that only one be used with any Stream_Monitor object; otherwise unpredictable interleaved text may be displayed.

    @return An {@link OutputStream} that can be used to write text to the monitor display. */ public Monitor_OutputStream Stream () {return Byte_Stream;} /** Get a Writer for writing to the monitor display.

    The stream is line-oriented: Each character written is buffered until an end-of-line sequence is encountered at which point the buffer is {@link #Write(String) written} to the monitor display. An end-of-line line sequence is any one of a line feed ('\n', 0x0A), a carriage return ('\r', 0x0D), or a carriage return followed immediately by a linefeed.

    N.B.: Because both the Writer returned by this method and the {@link #Stream()} are buffered output to the same monitor display it is recommended that only one be used with any Stream_Monitor object; otherwise unpredictable interleaved text may be displayed.

    @return A {@link Writer} that can be used to write text to the monitor display. */ public Monitor_Writer Writer () {return Character_Writer;} /** Set the maximum number of characters to be retained for scrolling.

    @param capacity The maximum number of characters to be retained. This value will be limited to not less than {@link #MIN_CAPACITY}. */ public Stream_Monitor Capacity ( int capacity ) { if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; Capacity = capacity; Remove_Excess_Capacity (); return this; } /** Get the maximum number of characters to be retained for scrolling.

    @return The maximum number of characters to be retained. @see #Capacity(int) */ public int Capacity () {return Capacity;} private void Remove_Excess_Capacity ( int additional ) { synchronized (Monitor_Document) { if ((Monitor_Document.getLength () + additional) > Capacity) { /* Remove the excess capacity. Starting at the position offset from the beginning of the document by the additonal amount minus the length of a new-line (NL) sequence - in case the excess ends with a NL - search for the next NL before the end of the document. Then remove text from the beginning of the document up to and including the NL that was found. If the additional amount is greater than the length of the document - which is very unlikely - remove all document text. */ int position = additional - NL_length, // Search start position. end = Monitor_Document.getLength (), // End of the document. last = end - NL_length; // Last search position. /* If any text is selected, unselect it. This was causing Stream_Monitor to freeze when removing excess capacity. */ if (Monitor_Text_Pane.getSelectionStart () != Monitor_Text_Pane.getSelectionEnd ()) Monitor_Text_Pane.setSelectionEnd (Monitor_Text_Pane.getSelectionStart ()); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Removing excess capacity -" + NL +" capacity = " + Capacity + NL +" document = " + end + NL +" addition = " + additional + NL +" begin position = " + position); if (additional < end && last >= 0) { if (position < 0) // New text length is less than NL length. position = 0; try { // Find the next NL. while (position < last && ! Monitor_Document.getText (position, NL_length) .equals (NL)) ++position; position += NL_length; } catch (BadLocationException e) { // Shouldn't happen. if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Invalid search position " + position); position = end; } } else // New text length is greater than the document length. position = end; if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" final position = " + position); try {Monitor_Document.remove (0, position);} catch (BadLocationException exception) { // Shouldn't happen: The remove position is within the document. if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Stream_Monitor.Write: Failed to remove " + position + " characters to first NL"); } } } // Monitor_Document synchronized. } private void Remove_Excess_Capacity () {Remove_Excess_Capacity (0);} /** Set the default character capacity.

    When a new Stream_Monitor is constructed its initial capacity will be set to the default capacity.

    @param capacity The initial capacity for a new Stream_Monitor. This value will be limited to not less than {@link #MIN_CAPACITY}. */ public static void Default_Capacity ( int capacity ) { if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY; Default_Capacity = capacity; } /** Get the default character capacity.

    @return The initial capacity for a new Stream_Monitor. @see #Default_Capacity(int) */ public static int Default_Capacity () {return Default_Capacity;} /** Get the number of characters in the monitor.

    N.B.: The amount of capacity used may be larger than the {@link #Capacity(int) capacity} set for the monitor.

    @return The amount of used character space in the monitor. @see #Write(String, AttributeSet) */ public int Used () {return Monitor_Document.getLength ();} /** Enable or disable auto-styling.

    If auto-styling is enabled, and a style is not specified, all text {@link #Write(String, AttributeSet) written} to the monitor display is first examined for an initial sequence that matches one of the text-to-style keys. If a match is found the associated style is applied to the displayed text.

    @param enable If true, auto-styling is enabled; false disables auto-styling. @return This Stream_Monitor. */ public Stream_Monitor Auto_Style ( boolean enable ) {Auto_Style = enable; return this;} /** Test if auto-styling mode is enabled

    @return true, auto-styling is enabled; false otherwise. @see #Auto_Style(boolean) */ public boolean Auto_Style () {return Auto_Style;} /** Add an auto-style text-to-style association.

    When auto-style is enabled each time text is {@link #Write(String) written} or the {@link #Stream() output stream} is flushed to the monitor display the text is checked for the specified prefix text and, if found, the corresponding style is applied.

    @param text The prefix String to be associated with a style. If null no association is made. @param style The AttributeSet to be associated with the text. If null no association is made. @return This Stream_Monitor. @see #Auto_Style(boolean) @see #Write(String, AttributeSet) */ public Stream_Monitor Auto_Style ( String text, AttributeSet style ) { synchronized (Auto_Style_Table) { try {Auto_Style_Table.put (text, style);} catch (NullPointerException e) {} } return this; } /** Get the style associated with a text prefix.

    @param text The prefix text that is associated with a style. @return The AttributeSet associated with the text. This will be null if there is no text-to-style association. @see #Auto_Style(boolean) */ public AttributeSet Auto_Style ( String text ) { synchronized (Auto_Style_Table) { try {return (AttributeSet)Auto_Style_Table.get (text);} catch (NullPointerException e) {return null;} } } /** Get the text-to-style association table.

    @return The Hastable containing the text-to-style associations. @see #Auto_Style(boolean) */ public Hashtable Auto_Style_Table () {return Auto_Style_Table;} /** Get the monitor's "File" menu.

    The File menu has "Save" and "Save As ..." menu items, in that order, that have action listeners that will {@link #Save_Monitor_Text(boolean) save the monitor text}. They have no mnemonics or accelerators.

    @return A JMenu. */ public JMenu File_Menu () {return File_Menu;} /** Get the monitor's "View" menu.

    The View menu has "Capacity ..." and "Clear" menu items, in that order, that have action listeners that will interactively set the monitor character {@link #Capacity(int) capacity} and {@link #Clear() clear} the monitor contents, respectively. They have no mnemonics or accelerators.

    @return A JMenu. */ public JMenu View_Menu () {return View_Menu;} /** Get the JScrollPane that contains the JTextPane where the monitor text is displayed.

    The StyledDocument managing the text can be obtained by:

    Stream_Monitor monitor = new Stream_Monitor(); ... StyledDocument document = (JTextPane)(monitor.Scroll_Pane().getViewport().getView()) .getStyledDocument();

    @return The JScrollPane that contains the JTextPane where the monitor text is displayed. */ public JScrollPane Scroll_Pane () {return Scroll_Pane;} /*============================================================================== GUI */ private void Menus () { JMenuItem menu_item; File_Menu = new JMenu ("File"); menu_item = new JMenuItem ("Save"); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Monitor_Text (false);}}); File_Menu.add (menu_item); menu_item = new JMenuItem ("Save As ..."); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Monitor_Text (true);}}); File_Menu.add (menu_item); View_Menu = new JMenu ("View"); menu_item = new JMenuItem ("Capacity ..."); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Set_Capacity ();}}); View_Menu.add (menu_item); menu_item = new JMenuItem ("Clear"); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Clear ();}}); View_Menu.add (menu_item); } private void Panels () { // Text pane. Monitor_Document = new DefaultStyledDocument (); Monitor_Text_Pane = new JTextPane (Monitor_Document); Scroll_Pane = new JScrollPane (Monitor_Text_Pane); setLayout (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; add (Scroll_Pane, location); // Auto-scroll controls. Caret caret = Monitor_Text_Pane.getCaret (); if (caret instanceof DefaultCaret) { Text_Caret = (DefaultCaret)caret; Default_Update_Policy = Text_Caret.getUpdatePolicy (); Scroll_Bar = Scroll_Pane.getVerticalScrollBar (); // Add a MouseListener to the Scroll_Bar. Scroll_Bar.addMouseListener (new MouseListener () { public void mousePressed (MouseEvent event) {} public void mouseReleased (MouseEvent event) {Page_Scrolled ();} public void mouseEntered (MouseEvent event) {} public void mouseExited (MouseEvent event) {} public void mouseClicked (MouseEvent event) {} }); // Add a MouseWheelListener to the Scroll_Pane. Scroll_Pane.addMouseWheelListener (new MouseWheelListener () {public void mouseWheelMoved (MouseWheelEvent event) {Page_Scrolled ();}}); } } /*------------------------------------------------------------------------------ Actions */ /** Save the current monitor text to a file.

    If interactive is true a JFileChooser will be used to select the file where the text will be saved. Otherwise the text will be saved to the previously selected file. If no file has been previously selected interactive mode will be used.

    The current monitor contents are written as plain text.

    If there was a problem writing the text to the file an {@link Dialog_Box#Error(String, Component) error dialog} will display the exception message.

    @param interactive If interactive file selection is to be used. */ public void Save_Monitor_Text ( boolean interactive ) { File file; if (! interactive && Monitor_Text_Filename == null) // No filename; force interactive operation. interactive = true; if (interactive) { JFileChooser file_chooser = new JFileChooser (CWD); file_chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); if (file_chooser.showSaveDialog (this) != JFileChooser.APPROVE_OPTION) return; file = file_chooser.getSelectedFile (); CWD = file.getParent (); Monitor_Text_Filename = file.getPath (); } else file = new File (Monitor_Text_Filename); // Write the monitor text to the file. synchronized (Monitor_Document) { try { BufferedWriter writer = new BufferedWriter (new FileWriter (file)); Monitor_Text_Pane.write (writer); writer.close (); } catch (IOException exception) { Dialog_Box.Error (ID + '\n' + "Unable to save the monitor text to " + file.getPath () + NL + NL + exception.getMessage (), this); } } } /** Save the current monitor text to a file.

    @param pathname The pathname to the file where the text is to be save. If null, interactive file selection will be used. @see #Save_Monitor_Text(boolean) */ public void Save_Monitor_Text ( String pathname ) { Monitor_Text_Filename = pathname; Save_Monitor_Text (false); } /** Save the current monitor text to a file.

    If a file has previously been selected for saving the monitor text then it is overwritten. Otherwise interactive file selection is used.

    @see #Save_Monitor_Text(boolean) */ public void Save_Monitor_Text () {Save_Monitor_Text (false);} private void Set_Capacity () { int used = Used (); double capacity; String value, capacity_value = String_Utilities.Amount_Magnitude (Capacity), message = "Current content - " + String_Utilities.Amount_Magnitude (used); if (used >= 1024) message += " (" + used + ')'; message += NL + "Monitor capacity (characters):"; while ((value = JOptionPane.showInputDialog (this, message, capacity_value)) != null) { if (value.length () == 0) break; if (! Character.isDigit (value.charAt (value.length () - 1)) && value.charAt (value.length () - 1) != '.') { switch (Character.toUpperCase (value.charAt (value.length () - 1))) { case 'K': used = 1024; break; case 'M': used = 1024 * 1024; break; default: Dialog_Box.Notice ("Please enter a numeric value.", this); continue; } value = value.substring (0, value.length () - 1); } else used = 1; try {capacity = Double.parseDouble (value);} catch (NumberFormatException exception) { Dialog_Box.Notice ("Please enter a numeric value.", this); continue; } capacity *= used; if (capacity < 0) { Dialog_Box.Notice ("A negative capacity can not be used.", this); continue; } if (capacity < MIN_CAPACITY) { Dialog_Box.Notice ("The minimum capacity is " + MIN_CAPACITY + " characters.", this); continue; } if (capacity >= Integer.MAX_VALUE) { Dialog_Box.Notice ("The value is too large.", this); continue; } Capacity ((int)capacity); break; } } /** Manage page scrolling event. The page has been scrolled, either by the scroll bar or by the mouse wheel. Check the scroll bar position and current document caret position, and if the scroll bar was moved to the end, move the caret to the end. Otherwise the scroll bar was moved away from the end. If the caret was at the end, move it away from the end. Otherwise, leave the caret where it is. */ private void Page_Scrolled () { if (Scroll_Bar.getMinimum () + Scroll_Bar.getValue () + Scroll_Bar.getVisibleAmount () + SCROLL_AT_BOTTOM_MARGIN >= Scroll_Bar.getMaximum ()) { // Scroll bar moved to end, set caret to end. Monitor_Text_Pane.setCaretPosition (Monitor_Text_Pane.getDocument ().getLength ()); Text_Caret.setVisible (false); } else if (Text_Caret.getMark () == Monitor_Text_Pane.getDocument ().getLength ()) { // Scroll bar moved away from end, if caret is at end, move it. Monitor_Text_Pane.setCaretPosition (Monitor_Text_Pane.getDocument ().getLength () - 1); Text_Caret.setVisible (false); } } /*============================================================================== Manipulators */ /** Clear the monitor of all text. */ public Stream_Monitor Clear () { synchronized (Monitor_Document) { try {Monitor_Document.remove (0, Monitor_Document.getLength ());} catch (BadLocationException exception) {} } Monitor_Text_Pane.setCaretPosition (0); return this; } /** Write text to the monitor.

    This method {@link #Write(String, AttributeSet) write}s the text with a null style.

    @param text A text String to be added to the end of the monitor display. @see #Write(String, AttributeSet) */ public Stream_Monitor Write ( String text ) {Character_Writer.Write (text, null); return this;} /** Write styled text to the monitor.

    If the style is null and {@link #Auto_Style(boolean) auto-styling} is enabled the initial portion of the text will be checked for an auto-style key. If one is found the associated style will be applied. N.B.: The style is applied to all of the text specified; auto-styling is not limited to the single line which begins with an auto-style prefix.

    If the amount of space {@link #Used() used} is greater than the {@link #Capacity() capacity} plus a {@link #MIN_CAPACITY} margin, monitor text is removed from the top (FIFO) up to and including the end-of-line characters to bring the amount used below the capacity.

    The text is appended to the bottom of the monitor with the style, if any, applied. The scroll pane is moved up so that the end of the text appears at the bottom of the display.

    A style is assembled using combinations of the numerous StyleConstants setXXX functions applied to a SimpleAttributeSet. For example:

    SimpleAttributeSet bold_red_text = new SimpleAttributeSet ();
    StyleConstants.setBold       (bold_red_text, true);
    StyleConstants.setForeground (bold_red_text, Color.RED);
    StyleConstants.setFontFamily (bold_red_text, "Monospaced");
    

    assembles a style that will produce text in a bold, red, monospaced font.

    @param text The text String to be appended to the monitor. @param style The AttributeSet to be applied to the text. This may be null if plain text is to be displayed. @see StyleConstants */ public Stream_Monitor Write ( String text, AttributeSet style ) {Character_Writer.Write (text, style); return this;} private void Write_to_Monitor ( String text, AttributeSet style ) { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Stream_Monitor.Write_to_Monitor:\n" + text); if (text == null || text.length () == 0) { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Stream_Monitor.Write_to_Monitor"); return; } if (Auto_Style && style == null) { synchronized (Auto_Style_Table) { Enumeration keys = Auto_Style_Table.keys (); while (keys.hasMoreElements ()) { String key = (String)keys.nextElement (); if (text.startsWith (key)) { style = (AttributeSet)Auto_Style_Table.get (key); break; } } } } if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Style " + style); Remove_Excess_Capacity (text.length ()); // Append the text on the event dispatcher thread. final String string = text; final AttributeSet attributes = style; SwingUtilities.invokeLater (new Runnable () { public void run () { /* if (Text_Caret != null && Scroll_Pane.getViewport ().getViewRect ().height != 0) { // Auto-scroll control. if ((DEBUG & DEBUG_AUTO_SCROLL) != 0) System.out.println ("--> Auto-scroll control:" + NL +" Viewport = " + Scroll_Pane.getViewport ().getViewRect () + NL +" ScrollBar -" + NL +" minimum = " + Scroll_Bar.getMinimum () + NL +" value = " + Scroll_Bar.getValue () + NL +" extent = " + Scroll_Bar.getVisibleAmount () + NL +" maximum = " + Scroll_Bar.getMaximum ()); if ((Scroll_Bar.getMinimum () +Scroll_Bar.getValue () +Scroll_Bar.getVisibleAmount () +SCROLL_AT_BOTTOM_MARGIN) >= Scroll_Bar.getMaximum ()) { if ((DEBUG & DEBUG_AUTO_SCROLL) != 0) System.out.println (" ScrollBar thumb at bottom"); // The scroll bar thumb is at the bottom position. Text_Caret.setUpdatePolicy (Default_Update_Policy); } else { if ((DEBUG & DEBUG_AUTO_SCROLL) != 0) System.out.println (" ScrollBar thumb not at bottom"); Text_Caret.setUpdatePolicy (DefaultCaret.NEVER_UPDATE); } } */ // Add the new text. try {Monitor_Document.insertString (Monitor_Document.getLength (), string, attributes);} catch (BadLocationException e) { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Stream_Monitor.Write: Failed to append " + string.length () + " characters"); } /* if (Text_Caret != null) { if (Text_Caret.getUpdatePolicy () != DefaultCaret.NEVER_UPDATE) { */ // Reposition at the end of the text pane. /* Monitor_Text_Pane.setCaretPosition (Monitor_Document.getLength ()); Scroll_Bar.setValue (Scroll_Bar.getMaximum () - Scroll_Bar.getVisibleAmount ()); */ /* } if ((DEBUG & DEBUG_AUTO_SCROLL) != 0) System.out.println ("<-- After insert:" + NL +" Viewport = " + Scroll_Pane.getViewport ().getViewRect () + NL +" ScrollBar -" + NL +" minimum = " + Scroll_Bar.getMinimum () + NL +" value = " + Scroll_Bar.getValue () + NL +" extent = " + Scroll_Bar.getVisibleAmount () + NL +" maximum = " + Scroll_Bar.getMaximum ()); } */ }}); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Stream_Monitor.Write"); } /*============================================================================== OutputStream implementation */ public class Monitor_Writer extends Writer implements Styled_Writer { private char[] Character_Buffer = new char[Stream_Buffer_Capacity]; private int Character_Count = 0; /** Write a character.

    Each character written is buffered until an end-of-line sequence is encountered at which point the buffer is {@link #flush() flushed} to the display. An end-of-line line sequence is any one of a line feed ('\n', 0x0A), a carriage return ('\r', 0x0D), or a carriage return followed immediately by a linefeed.

    @param character An integer value of which only the least significant 16-bits are used. */ public void write ( int character ) {put_character ((char)(character & 0xFFFF));} private void put_character ( char character ) { if (character != 0x0D && Character_Count > 0 && Character_Buffer[Character_Count - 1] == 0x0D) // CR not followed by a LF. flush (); Character_Buffer[Character_Count++] = character; if (character == 0x0A || // LF Character_Count == Character_Buffer.length) flush (); } /** Write an array of characters.

    The characters are buffered until an end-of-line sequence is encountered or the buffer is full at which point the buffer is {@link #flush() flushed} to the display. An end-of-line line sequence is any one of a line feed ('\n', 0x0A), a carriage return ('\r', 0x0D), or a carriage return followed immediately by a linefeed.

    @param characters The char array containing the characters to be written. If null or empty nothing is done. @param offset Array offset from which to start writing characters. @param amount The number of characters to write. If zero nothing is done. @throws IndexOutOfBoundsException If the offset or amount are negative or the offset plus the amount is greater than the length of the array. */ public void write ( char[] characters, int offset, int amount ) { if (characters == null || characters.length == 0 || amount == 0) return; if (offset < 0 || amount < 0 || (offset + amount) > characters.length) throw new IndexOutOfBoundsException (ID + NL + "Invalid values for " + characters.length + " character array content -" + NL + " Array offset " + offset + " and amount " + amount + '.'); amount += offset; while (offset < amount) put_character (characters[offset++]); } /** Write styled text to the monitor.

    Any currently buffered characters are flushed before the text is written to the monitor.

    @param text The text String to be appended to the monitor. @param style The AttributeSet to be applied to the text. This may be null if plain text is to be displayed. @see Stream_Monitor#Write(String, AttributeSet) */ public Monitor_Writer Write ( String text, AttributeSet style ) { Write_to_Monitor (text, style); return this; } /** Write text to the monitor.

    Any currently buffered characters are flushed before the text is written to the monitor.

    @param text The text String to be appended to the monitor. @see Stream_Monitor#Write(String) */ public Monitor_Writer Write ( String text ) { Write_to_Monitor (text, null); return this; } /** Flush the currently buffered characters to the monitor display.

    The currently buffered characters are converted to a String that is {@link #Write(String, AttributeSet) written} to the monitor display. The text is eligible for {@link #Auto_Style(boolean) auto-style} processing, if enabled. */ public void flush () { if (Character_Count > 0) { Write_to_Monitor (new String (Character_Buffer, 0, Character_Count), null); Character_Count = 0; } } /** The currently buffered characters are flushed, but the writer can still be written to. */ public void close () {flush ();} }; // End of Monitor_Writer class. /*============================================================================== Writer implementation */ public class Monitor_OutputStream extends OutputStream { private byte[] Byte_Buffer = new byte[Stream_Buffer_Capacity]; private int Byte_Count = 0; /** Write a byte to the monitor.

    Each byte written is buffered until an end-of-line sequence is encountered or the buffer is full at which point the buffer is {@link #flush() flushed} to the display. An end-of-line line sequence is any one of a line feed ('\n', 0x0A), a carriage return ('\r', 0x0D), or a carriage return followed immediately by a linefeed.

    @param value An integer value of which only the least significant byte is buffered. */ public synchronized void write ( int value ) { if (value != 0x0D && Byte_Count > 0 && Byte_Buffer[Byte_Count - 1] == 0x0D) // CR not followed by a LF. flush (); Byte_Buffer[Byte_Count++] = (byte)(value & 0xFF); if (value == 0x0A || Byte_Count == Byte_Buffer.length) // LF. flush (); } /** Flush the currently buffered bytes to the monitor display.

    The currently buffered bytes are converted to a String using the default character encoding. This text is {@link #Write(String, AttributeSet) written} to the monitor display. The text is eligible for {@link #Auto_Style(boolean) auto-style} processing, if enabled. */ public void flush () { if (Byte_Count > 0) { Write_to_Monitor (new String (Byte_Buffer, 0, Byte_Count), null); Byte_Count = 0; } } /** The currently buffered characters are flushed, but the writer can still be written to. */ public void close () {flush ();} }; // End of Monitor_OutputStream class. } pirl-2.3.8/PIRL/Viewers/URL_Dialog.java0000644000175000017500000003711111742735303017332 0ustar mathieumathieu/* URL_Dialog PIRL CVS ID: URL_Dialog.java,v 1.5 2012/04/16 06:22:59 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import java.net.URL; import java.net.MalformedURLException; import javax.swing.*; import java.awt.*; import java.awt.event.*; /** URL_Dialog provides a dialog used to interactively obtain the information needed to construct a URL.

    URL_Dialog is a reusable dialog. Once constructed it only needs to be setVisible to be activated. If it had been used previously the previous field values that were accepted will remain. The current URL definition is renewed when the Accept button is pressed and the information is valid. To be valid, the Protocol and Hostname fields must not be empty and the Port number field must either be empty or an integer number. The default protocol is http, but may be overridden.

    @author Bradford Castalia, UA/PIRL @version 1.5 */ public class URL_Dialog extends JDialog { /** Class name and version identification. */ public static final String ID = "PIRL.Viewers.URL_Dialog (1.5 2012/04/16 06:22:59)"; /** Default protocol. */ public static final String DEFAULT_PROTOCOL = "http"; private static String Default_Protocol = DEFAULT_PROTOCOL; // Information fields. private JTextField Protocol_Field = new JTextField (5), Hostname_Field = new JTextField (30), Port_Number_Field = new JTextField (5), Pathname_Field = new JTextField (40); // The Current URL constructed from the last Accept action. private URL Current_URL = null; // Icon displayed on the dialog box. private Icon Decorative_Icon = null; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_GET = 1 << 1, DEBUG_UI = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a URL_Dialog.

    @param title The title String for the dialog window. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param owner The Frame with which the dialog is associated, which may be null. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public URL_Dialog ( String title, Icon icon, Frame owner, boolean modal ) { super (owner, (title == null ? "URL" : title), modal); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> URL_Dialog"); Decorative_Icon = icon; // Build the panels. getContentPane ().add (Panels (), BorderLayout.CENTER); pack (); getRootPane ().setMinimumSize (getContentPane ().getSize ()); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< URL_Dialog"); } /** Constructs a URL_Dialog.

    @param title The title String for the dialog window. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public URL_Dialog ( String title, Icon icon, boolean modal ) {this (title, icon, null, modal);} /** Constructs a URL_Dialog.

    @param title The title String for the dialog window. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public URL_Dialog ( String title, boolean modal ) {this (title, null, null, modal);} /** Constructs a URL_Dialog.

    @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public URL_Dialog ( boolean modal ) {this (null, null, null, modal);} /** Constructs a default URL_Dialog. */ public URL_Dialog () {this (null, null, null, false);} /*------------------------------------------------------------------------------ PUBLIC INTERFACE */ /** Get a URL.

    A modal URL_Dialog is constructed from which to obtain a URL.

    @param title The title String for the dialog window. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param parent The parent window that dialog is associated with, which may be null. @return A URL constructed from the information obtained. This will be null if the user Canceled the dialog. */ public static URL Get_URL ( String title, Icon icon, JFrame parent ) { if ((DEBUG & DEBUG_GET) != 0) System.out.println (">>> URL_Dialog.Get_URL"); URL_Dialog URL_dialog = new URL_Dialog (title, icon, parent, true); URL_dialog.setVisible (true); if ((DEBUG & DEBUG_GET) != 0) System.out.println ("<<< URL_Dialog.Get_URL"); return URL_dialog.Current_URL; } /** Get a URL.

    A default URL_Dialog is used to obtain a URL.

    @return A URL constructed from the information obtained. This will be null if the user Canceled the dialog. @see #Get_URL(String, Icon, JFrame) */ public static URL Get_URL () {return Get_URL (null, null, null);} /** Get the current URL.

    The current URL is produced after each successful accept action.

    @return The current URL. This wil be null if no URL has yet been constructed. */ public URL Current_URL () {return Current_URL;} /** Get the default protocol.

    @return The default protocol String. */ public static String Default_Protocol () {return Default_Protocol;} /** Set the default protocol.

    Trailing "://" characters are stripped off.

    @param protocol The default protocol String. A null or empty value will set the {@link #DEFAULT_PROTOCOL}. */ public static void Default_Protocol ( String protocol ) {Default_Protocol = Check_Protocol (protocol);} /** Set the Protocol field entry.

    Trailing "://" characters are stripped off.

    @param protocol The default protocol String. A null or empty value will set the {@link #DEFAULT_PROTOCOL}. @return This URL_Dialog. */ public URL_Dialog Protocol ( String protocol ) { Protocol_Field.setText (Check_Protocol (protocol)); return this; } /** Set the Hostname field entry.

    @param hostname The hostname String. @return This URL_Dialog. */ public URL_Dialog Hostname ( String hostname ) { Hostname_Field.setText (hostname); return this; } /** Set the Port number field entry.

    @param port The port number. A negative value clears the field. @return This URL_Dialog. */ public URL_Dialog Port_Number ( int port ) { if (port < 0) Port_Number_Field.setText (null); else Port_Number_Field.setText (Integer.toString (port)); return this; } /** Set the Pathname field entry.

    @param pathname The pathname String. @return This URL_Dialog. */ public URL_Dialog Pathname ( String pathname ) { Pathname_Field.setText (pathname); return this; } /*============================================================================== Panels */ private JPanel Panels () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> URL_Dialog.Panels"); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Icon or empty space. location.gridx = GridBagConstraints.RELATIVE; location.gridy = GridBagConstraints.RELATIVE; location.gridwidth = 1; location.gridheight = 3; location.weightx = 0.0; location.weighty = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.NORTH; location.insets = new Insets (10, 10, 10, 5); if (Decorative_Icon == null) // Place holder. panel.add (Box.createHorizontalGlue (), location); else // Decorative icon. panel.add (new JLabel (Decorative_Icon), location); // Protocol. Protocol_Field.setText (Default_Protocol); location.gridheight = 1; location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (10, 0, 5, 5); panel.add (new JLabel("Protocol:"), location); Hostname_Field.setToolTipText ("Protocol for the server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (10, 0, 5, 10); panel.add (Protocol_Field, location); // Hostname. location.gridheight = 1; location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel("Hostname:"), location); Hostname_Field.setToolTipText ("Host name of the server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 10); panel.add (Hostname_Field, location); // Port number. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel("Port number:"), location); Port_Number_Field.setToolTipText ("Port number for the server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 10); panel.add (Port_Number_Field, location); // Pathname. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 10, 5); panel.add (new JLabel("Pathname:"), location); Pathname_Field.setToolTipText ("Path to the file to be accessed"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 10, 10); panel.add (Pathname_Field, location); // Buttons panel: JPanel button_panel = new JPanel (new GridBagLayout ()); JButton button; location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 0, 0); // Accept. button = new JButton ("Accept"); button.setMnemonic ('A'); button.setToolTipText ("Use the current URL"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept (true);}}); button.setDefaultCapable (true); getRootPane ().setDefaultButton (button); location.anchor = GridBagConstraints.WEST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); // Clear. button = new JButton ("Clear"); button.setMnemonic ('C'); button.setToolTipText ("Clear all fields"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Clear ();}}); location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.CENTER; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); // Cancel. button = new JButton ("Cancel"); button.setMnemonic ('n'); button.setToolTipText ("Cancel the operation"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept (false);}}); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.EAST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 10, 10, 10); panel.add (button_panel, location); if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< URL_Dialog.Panels"); return panel; } /*============================================================================== Actions */ private void Accept ( boolean accept ) { if (accept) { String protocol; if ((protocol = Protocol_Field.getText ()).length () == 0) { Dialog_Box.Notice ("The server protocol must be specified.", this); Protocol_Field.setText (Default_Protocol); return; } String host; if ((host = Hostname_Field.getText ()).length () == 0) { Dialog_Box.Notice ("The name of the server host must be specified.", this); return; } int port = -1; String value; if ((value = Port_Number_Field.getText ()).length () != 0) { try {port = Integer.parseInt (value);} catch (NumberFormatException exception) { Dialog_Box.Error ("The Port value must be an integer number.", this); return; } } String pathname = Pathname_Field.getText (); if (pathname.length () != 0 && pathname.charAt (0) != '/') pathname = "/" + pathname; URL url = null; try {url = new URL (protocol, host, port, pathname);} catch (MalformedURLException exception) { // This shouldn't happen as long as the protocol is acceptable. Dialog_Box.Error ("Unable to construct a URL from the information provided.\n" + exception.getMessage (), this); return; } Current_URL = url; } else { if (Current_URL != null) { // Restore the previous values. Hostname_Field.setText (Current_URL.getHost ()); int port = Current_URL.getPort (); if (port < 0) Port_Number_Field.setText (null); else Port_Number_Field.setText (Integer.toString (port)); Pathname_Field.setText (Current_URL.getFile ()); } else Clear (); Current_URL = null; } setVisible (false); } /** Clears the fields.

    The Protocol field will be reset to the {@link #Default_Protocol() default} value. */ public void Clear () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">-< URL_Dialog.Clear"); Protocol_Field.setText (Default_Protocol); Hostname_Field.setText (null); Port_Number_Field.setText (null); Pathname_Field.setText (null); } /*============================================================================== Helpers */ private static String Check_Protocol ( String protocol ) { if (protocol != null) { // Remove trailing "://" if present. int index = protocol.length () - 1; while (index >= 0 && protocol.charAt (index) == '/') --index; while (index >= 0 && protocol.charAt (index) == ':') --index; if (++index < protocol.length ()) protocol = protocol.substring (0, index); if (protocol.length () != 0) return protocol; } return DEFAULT_PROTOCOL; } /*============================================================================== Application test stub */ public static void main (String[] arguments) { if (DEBUG != DEBUG_OFF) System.out.println ("*** " + ID); try { URL url = null; url = Get_URL (); System.out.println ("URL - " + url); } catch (Exception exception) { System.out.println (exception.getMessage ()); System.exit (1); } System.exit (0); } } // End of URL_Dialog class. pirl-2.3.8/PIRL/Viewers/Memory_Chart_Panel.java0000644000175000017500000003745111742735302021127 0ustar mathieumathieu/* Memory_Chart_Panel CVS ID: Memory_Chart_Panel.java,v 1.4 2012/04/16 06:22:58 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JPanel; import javax.swing.BorderFactory; import javax.swing.JPopupMenu; import javax.swing.JMenuItem; import javax.swing.JCheckBoxMenuItem; import javax.swing.JOptionPane; import javax.swing.JFileChooser; import javax.swing.Box; import javax.swing.InputMap; import javax.swing.ActionMap; import javax.swing.Action; import javax.swing.AbstractAction; import javax.swing.KeyStroke; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintStream; import java.io.FileOutputStream; /** A Memory_Chart_Panel provides panel for a Memory_Chart.

    The panel may optionally be provided with a popup menu that includes Memory_Chart controls that are also bound to accelerator keys. In addition to basic Memory_Chart controls, a Save History action is provided that will save the current memory sampling history to a file in comma separated value (CSV) format.

    @author Bradford Castalia - UA/PIRL @version 1.4 @see Memory_Chart */ public class Memory_Chart_Panel extends JPanel { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Memory_Chart_Panel (1.4 2012/04/16 06:22:58)"; /** The default {@link Memory_Chart} sampling rate. */ public static final int DEFAULT_SAMPLING_RATE = 1; private Memory_Chart Chart; private JPopupMenu Popup_Menu = null; private JMenuItem Start_Stop_MenuItem; private JCheckBoxMenuItem Annotation_MenuItem, Seconds_Scale_MenuItem; /** Menu mnemonic and keyboard accellerator keys.

    When the {@link #Menu() menu} is visible, each key combined with the ALT key will select the corresponding menu item. When the component has focus, each key combined with the CTL (control) key will cause the corresponding action to occur. */ public static final int STOP_START_KEY = KeyEvent.VK_S, SAMPLING_RATE_KEY = KeyEvent.VK_R, ANNOTATION_KEY = KeyEvent.VK_A, SECONDS_SCALE_KEY = KeyEvent.VK_E, SAVE_HISTORY_KEY = KeyEvent.VK_H; private JFileChooser File_Chooser = new JFileChooser (System.getProperty ("user.dir")); /*============================================================================== Constructors */ /** Construct a Memory_Chart_Panel using a sampling rate and optional popup menu.

    A border with the title "Memory Use" is placed around a {@link Memory_Chart} that uses the specified sampling rate. A popup {@link #Menu() menu} is provided if requested.

    @param rate The memory sampling rate for the Memory_Chart. @param provide_menu If true, a popup menu is provided. */ public Memory_Chart_Panel ( int rate, boolean provide_menu ) { setLayout (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Memory chart. Chart = new Memory_Chart (rate); setBorder (BorderFactory.createTitledBorder ("Memory Use")); location.fill = GridBagConstraints.BOTH; location.weightx = location.weighty = 1.0; add (Chart, location); if (provide_menu) { // Popup menu. Popup_Menu = Menu (); addMouseListener (new Popup_Listener ()); } } /** Construct a Memory_Chart_Panel using a sampling rate.

    A popup {@link #Menu() menu} is provided.

    @param rate The memory sampling rate for the Memory_Chart. */ public Memory_Chart_Panel ( int rate ) {this (rate, true);} /** Construct a Memory_Chart_Panel with an optional popup menu.

    The Memory_Chart will use the {@link #DEFAULT_SAMPLING_RATE}.

    @param provide_menu If true, a popup menu is provided. */ public Memory_Chart_Panel ( boolean provide_menu ) {this (DEFAULT_SAMPLING_RATE, provide_menu);} /** Construct a Memory_Chart_Panel with a popup menu and using the {@link #DEFAULT_SAMPLING_RATE}. */ public Memory_Chart_Panel () {this (DEFAULT_SAMPLING_RATE, true);} /*============================================================================== Accessors */ /** Test if the Memory_Chart {@link Memory_Chart#Annotation(boolean) annotation} is enabled.

    @return true if the annotation is enabled; false otherwise. */ public boolean Annotation () {return Chart.Annotation ();} /** Test if the Memory_Chart {@link Memory_Chart#Seconds_Scale(boolean) seconds scale} is enabled.

    @return true if the seconds scale is enabled; false otherwise. */ public boolean Seconds_Scale () {return Chart.Seconds_Scale ();} /** Get the memory sampling rate.

    @return The time, in seconds, between memory samples. @see Memory_Chart#Rate(int) */ public int Rate () {return Chart.Rate ();} /** Get the previous memory sampling rate.

    @return The previous memory history sampling rate. This will be zero only if there was no non-zero previous sampling rate. @see Memory_Chart#Previous_Rate() */ public int Previous_Rate () {return Chart.Previous_Rate ();} /** Get the amount of memory allocated by the Java runtime environment but not currently in use.

    @return The amount, in bytes, of allocated but free memory. @see Memory_History#Available_Memory() */ public static long Free_Memory () {return Memory_History.Free_Memory ();} /** Get the amount of memory currently in use by the Java runtime environment.

    @return The amount, in bytes, of allocated memory. @see Memory_History#Available_Memory() */ public static long Allocated_Memory () {return Memory_History.Allocated_Memory ();} /** Get the total amount of memory available to the Java runtime environment.

    @return The total amount of memory, in bytes, available to the Java runtime environment. @see Memory_History#Available_Memory() */ public static long Available_Memory () {return Memory_History.Available_Memory ();} /*============================================================================== Menu */ /** Get a popup menu with Memory_Chart controls.

    Controls to {@link Memory_Chart#Stop() stop} and {@link Memory_Chart#Start() start} memory sampling, set the {@link Memory_Chart#Rate(int) sampling rate}, enable and disable the {@link Memory_Chart#Annotation(boolean) annotations} and {@link Memory_Chart#Seconds_Scale(boolean) seconds scale}, and save the current {@link Memory_History#History() sampling history} to a file are provided.

    The menu actions are also bound to keyboard accelerators.

    The Save History action will use a file chooser to select a file in which the current memory sampling history will be written in comma separated values (CSV) format. The first line provides the names of the values on the following lines (one line per sample): "Sample" is the sample index, starting with zero; "Allocated" followed by a slash characater ('/') and the total amount of memory available is the amount of {@link Memory_Chart#Allocated_Memory() allocated memory}; and "Free" is the amount of {@link Memory_Chart#Free_Memory() free memory} in the allocated pool.

    @return A JPopupMenu. */ public JPopupMenu Menu () { if (Popup_Menu == null) { Popup_Menu = new JPopupMenu ("Chart Controls"); JMenuItem menu_item; Action action; InputMap input_map = getInputMap (); ActionMap action_map = getActionMap (); KeyStroke keystroke; Start_Stop_MenuItem = new JMenuItem ((Chart.Rate () == 0) ? "Start" : "Stop"); Start_Stop_MenuItem.setMnemonic (STOP_START_KEY); keystroke = KeyStroke.getKeyStroke (STOP_START_KEY, ActionEvent.CTRL_MASK); Start_Stop_MenuItem.setAccelerator (keystroke); action = new AbstractAction () {public void actionPerformed (ActionEvent event) {Run (Chart.Rate () == 0);}}; Start_Stop_MenuItem.addActionListener (action); input_map.put (keystroke, "Run"); action_map.put ("Run", action); Popup_Menu.add (Start_Stop_MenuItem); menu_item = new JMenuItem ("Sampling rate ..."); menu_item.setMnemonic (SAMPLING_RATE_KEY); keystroke = KeyStroke.getKeyStroke (SAMPLING_RATE_KEY, ActionEvent.CTRL_MASK); menu_item.setAccelerator (keystroke); action = new AbstractAction () {public void actionPerformed (ActionEvent event) {Select_Rate ();}}; menu_item.addActionListener (action); input_map.put (keystroke, "Rate"); action_map.put ("Rate", action); Popup_Menu.add (menu_item); Annotation_MenuItem = new JCheckBoxMenuItem ("Annotation", Chart.Annotation ()); Annotation_MenuItem.setMnemonic (ANNOTATION_KEY); keystroke = KeyStroke.getKeyStroke (ANNOTATION_KEY, ActionEvent.CTRL_MASK); Annotation_MenuItem.setAccelerator (keystroke); action = new AbstractAction () {public void actionPerformed (ActionEvent event) {Annotation (! Chart.Annotation ());}}; Annotation_MenuItem.addActionListener (action); input_map.put (keystroke, "Annotation"); action_map.put ("Annotation", action); Popup_Menu.add (Annotation_MenuItem); Seconds_Scale_MenuItem = new JCheckBoxMenuItem ("Seconds Scale", Chart.Seconds_Scale ()); Seconds_Scale_MenuItem.setMnemonic (SECONDS_SCALE_KEY); keystroke = KeyStroke.getKeyStroke (SECONDS_SCALE_KEY, ActionEvent.CTRL_MASK); Seconds_Scale_MenuItem.setAccelerator (keystroke); action = new AbstractAction () {public void actionPerformed (ActionEvent event) {Seconds_Scale (! Chart.Seconds_Scale ());}}; Seconds_Scale_MenuItem.addActionListener (action); input_map.put (keystroke, "Seconds"); action_map.put ("Seconds", action); Popup_Menu.add (Seconds_Scale_MenuItem); menu_item = new JMenuItem ("Save History ..."); menu_item.setMnemonic (SAVE_HISTORY_KEY); keystroke = KeyStroke.getKeyStroke (SAVE_HISTORY_KEY, ActionEvent.CTRL_MASK); menu_item.setAccelerator (keystroke); action = new AbstractAction () {public void actionPerformed (ActionEvent event) {Save_History ();}}; menu_item.addActionListener (action); input_map.put (keystroke, "History"); action_map.put ("History", action); Popup_Menu.add (menu_item); } return Popup_Menu; } /*------------------------------------------------------------------------------ Actions */ /** Enable or disable the operation of the Memory_Chart.

    @param run If true, memory sampling will be {@link Memory_Chart#Start() started} if it is not already running. If false, sampling will be {@link Memory_Chart#Stop() stopped}. @return This Memory_Chart_Panel. */ public Memory_Chart_Panel Run ( boolean run ) { if (run) { if (Chart.Rate () == 0) { Chart.Start (); Start_Stop_MenuItem.setText ("Stop"); } } else if (Chart.Rate () != 0) { Chart.Stop (); Start_Stop_MenuItem.setText ("Start"); } return this; } /** Set the memory sampling rate.

    @param rate The memory {@link Memory_Chart#Rate(int) sampling rate}. @return This Memory_Chart_Panel. */ public Memory_Chart_Panel Rate ( int rate ) { Chart.Rate (rate); return this; } /** Use an input dialog to select the memory sampling rate. */ private void Select_Rate () { String rate = JOptionPane.showInputDialog (this, "Memory chart sampling rate (seconds):", String.valueOf (Chart.Rate ())); if (rate != null) { try {Chart.Rate (Integer.parseInt (rate));} catch (NumberFormatException exception) { Dialog_Box.Error (rate + " is not a valid sampling rate number.", this); } } } /** Enable or disable the Memory_Chart annotations.

    @param enabled If true, chart {@link Memory_Chart#Annotation(boolean) annotations} will be shown. If false they will not be shown. @return This Memory_Chart_Panel. */ public Memory_Chart_Panel Annotation ( boolean enabled ) { Chart.Annotation (enabled); Annotation_MenuItem.setSelected (enabled); return this; } /** Enable or disable the Memory_Chart seconds scale.

    @param enabled If true, chart {@link Memory_Chart#Seconds_Scale(boolean) seconds scale} will be shown. If false it will not be shown. @return This Memory_Chart_Panel. */ public Memory_Chart_Panel Seconds_Scale ( boolean enabled ) { Chart.Seconds_Scale (enabled); Seconds_Scale_MenuItem.setSelected (enabled); return this; } /** Use a file chooser to specify a file in which the memory sampling history will be saved. */ private void Save_History () { if (File_Chooser.showSaveDialog (this) != JFileChooser.APPROVE_OPTION) return; File file = File_Chooser.getSelectedFile (); try {CWD (file);} catch (FileNotFoundException exception) { Dialog_Box.Notice ("The pathname to " + file.getPath () + '\n' +"does not exist.\n\n" + exception.getMessage (), this); return; } if (file.exists ()) { if (file.isDirectory ()) { Dialog_Box.Notice ("Can't replace directory " + file.getAbsolutePath (), this); return; } if (! Dialog_Box.Confirm ("Replace " + file.getAbsolutePath () + '?', this)) return; } try { PrintStream output = new PrintStream (new FileOutputStream (file), true); long[][] history = Chart.getModel ().History (); output.println ("Sample,Allocated/" + Chart.Available_Memory () + ",Free"); for (int index = 0; index < history.length; index++) output.println (String.valueOf (index) + ',' +history[index][0] + ',' + history[index][1]); output.close (); if (output.checkError ()) Dialog_Box.Error ("There was a problem writing the file " + file.getAbsolutePath () + "\n\n", this); } catch (FileNotFoundException exception) { Dialog_Box.Error ("Can't open file " + file.getAbsolutePath () + "\n\n" + exception.getMessage (), this); } catch (SecurityException exception) { Dialog_Box.Error ("Denied write access to file " + file.getAbsolutePath () + "\n\n" + exception.getMessage (), this); } } /** Sets the current working directory for finding parameter files.

    @param file A File to which to set the CWD. If null, the "user.dir" System property will be used. If the file does not refer to an existing directory, the file's parent will be used. @return This Parameter_View. @throws FileNotFoundException If neither the file nor the file's parent refers to an existing directory. */ public Memory_Chart_Panel CWD ( File file ) throws FileNotFoundException { if (file == null) file = new File (System.getProperty ("user.dir")); if (! file.isAbsolute ()) file = file.getAbsoluteFile (); if (! file.isDirectory ()) // Can only set directories. file = file.getParentFile (); if (file.exists ()) File_Chooser.setCurrentDirectory (file); else throw new FileNotFoundException (ID + '\n' +"Can't set working directory to " + file.getAbsolutePath () + '\n' +"No such directory."); return this; } /*.............................................................................. Popup Menu Listener */ /** The Popup_Listener is a MouseAdapter used to listen for mouse events. */ class Popup_Listener extends MouseAdapter { public void mousePressed (MouseEvent event) {maybeShowPopup (event);} public void mouseReleased (MouseEvent event) {maybeShowPopup (event);} private void maybeShowPopup ( MouseEvent event ) { if (event.isPopupTrigger ()) Popup_Menu.show (event.getComponent (), event.getX (), event.getY ()); } } } pirl-2.3.8/PIRL/Viewers/Value_Pane.java0000644000175000017500000001762011742735303017433 0ustar mathieumathieu/* Value_Pane PIRL CVS ID: Value_Pane.java,v 1.8 2012/04/16 06:22:59 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.PVL.*; import javax.swing.JTree; import javax.swing.event.*; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.tree.*; import javax.swing.JScrollPane; import javax.swing.JFrame; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; /** A Value_Pane provides a view of an array Value.

    The display uses a JTree to represent the array of Values. Any Value in the array that is itself an array may be expanded to display its contents. Each Value is shown as the String provided by the Value's toString method.

    This Swing GUI can be employed whenever the display of a Value array is needed by an application. A main method is provided using this class as an application for viewing the array Values from a file containing PVL statements.

    @see Value @see JTree @author Bradford Castalia, UA/PIRL @version 1.8 */ public class Value_Pane extends JScrollPane { private static final String ID = "PIRL.Viewers.Value_Pane (1.8 2012/04/16 06:22:59)"; /** Default tree line style. */ public static String Default_Line_Style = Parameter_Pane.Default_Line_Style; // The JTree view. private JTree Values_Tree; // The Value being viewed. private Value The_Value = null; /* The Parameter_Pane that created this Value_Pane, if any. N.B.: "Enclosed" does not necessarily mean that this Value_Pane is actually enclosed in a Parameter_Pane. For example, it may be enclosed within a seperate JFrame such as a Value_View. */ private Parameter_Pane Enclosing_Parameter_Pane = null; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Value_Pane from the specified Value.

    @param value The Value to be displayed. @throws PVL_Exception If the Value is not an ARRAY type. The message is BAD_ARGUMENT. */ public Value_Pane ( Value value ) { Value (value); } /** Creates a Value_Pane from the specified Value.

    @param value The Value to be displayed. @param enclosing_parameter_pane The parent Parameter_Pane that interacts with this Value_Pane. May be null if there is no associated Parameter_Pane. @throws PVL_Exception If the Value is not an ARRAY type. The message is BAD_ARGUMENT. */ public Value_Pane ( Value value, Parameter_Pane enclosing_parameter_pane ) { Value (value); Enclosing_Parameter_Pane = enclosing_parameter_pane; } /*============================================================================== Acessors */ /** Gets the Value being viewed.

    @return The Value viewed in this Value_Pane. */ public Value Value () {return The_Value;} /** Sets the Value to be viewed.

    @param value The new Value to View. @return This Value_Pane. */ public Value_Pane Value ( Value value ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) { System.out.println (">>> Value_Pane.Value: value -"); try {value.Write ();} catch (Exception e) {} } Value container = new Value (); try {container.Add (value);} catch (PVL_Exception exception) { // This shouldn't happen. throw new Error ("Unable to assemble a container Value!\n\n" + ID + '\n' + exception.getMessage ()); } The_Value = value; if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) { System.out.println (" Value_Pane.Value: container of " + container.getChildCount () + " values -"); try {container.Write ();} catch (Exception e) {} } // Create the JTree. Values_Tree = new JTree (container); Values_Tree.addTreeWillExpandListener (new Tree_Expansion_Listener ()); Values_Tree.setRootVisible (false); Values_Tree.setShowsRootHandles (true); ((DefaultTreeCellRenderer)Values_Tree.getCellRenderer ()) .setLeafIcon (null); Values_Tree.getSelectionModel ().setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); Line_Style (Default_Line_Style); // Add the tree to the enclosing scroll pane. setViewportView (Values_Tree); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Value_Pane.Value"); return this; } /** Sets the Value to be viewed.

    @param value The new Value to View. @return This Value_Pane. @deprecated Replaced by {@link #Value(Value)} */ public Value_Pane Reload ( Value value ) {return Value (value);} /** Gets the "JTree.lineStyle" property currently being used.

    @return The "JTree.lineStyle" property String in effect. */ public String Line_Style () {return (String)Values_Tree.getClientProperty ("JTree.lineStyle");} /** Sets the "JTree.lineStyle" property to the specified style.

    @param style The String describing the style to use. @see javax.swing.JComponent#putClientProperty(Object, Object) */ public void Line_Style ( String style ) {Values_Tree.putClientProperty ("JTree.lineStyle", style);} /** Gets the JTree being used to represent the Value in the display.

    @return The JTree viewed in this Value_Pane. */ public JTree Tree () {return Values_Tree;} /*=***************************************************************************** Tree_Expansion_Listener */ /** A TreeWillExpandListener to handle expand and collapse of the Values_Tree. */ private class Tree_Expansion_Listener implements TreeWillExpandListener { /* The amount of time, in milliseconds, that that must have transpired before the top level root row is allowed to collapse. */ private static final int ROOT_PATH_COLLAPSE_DELAY = 50; // The Values_Tree path for the top level row. private TreePath Root_Path = null; // The time when the top level root row was expanded private long Root_Path_Expand_Time = 0; /** Prevents the top level root path from being collapsed within {@link #ROOT_PATH_COLLAPSE_DELAY} milliseconds of it having been first expanded.

    @param event The TreeExpansionEvent. */ public void treeWillCollapse ( TreeExpansionEvent event ) throws ExpandVetoException { if (event.getPath () == Root_Path && System.currentTimeMillis () < (Root_Path_Expand_Time + ROOT_PATH_COLLAPSE_DELAY)) throw new ExpandVetoException (event); } /** Registers the Values_Tree row that is expanded with an associated Parameter_Pane.

    The path to the top level root row is set and the the first time this method is used, and

    @param event The TreeExpansionEvent. */ public void treeWillExpand ( TreeExpansionEvent event ) { if (Enclosing_Parameter_Pane != null) Enclosing_Parameter_Pane.Last_Value_Path_Expanded (event.getPath ()); if (Root_Path == null) { // Only occurs when the top level root path is expanded. Root_Path = event.getPath (); Root_Path_Expand_Time = System.currentTimeMillis (); } } } // End of class Tree_Expansion_Listener. } // End of class Value_Pane. pirl-2.3.8/PIRL/Viewers/Projection_Viewer.java0000644000175000017500000003546711742735303021062 0ustar mathieumathieu/* Projection_Viewer PIRL CVS ID: Projection_Viewer.java,v 1.5 2012/04/16 06:22:59 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.Image_Tools.Projection; import PIRL.PVL.Parameter; import PIRL.PVL.Parser; import PIRL.PVL.PVL_Exception; import PIRL.Utilities.Streams; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JButton; import javax.swing.UIManager; import java.awt.Container; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Point; import java.awt.geom.Point2D; import java.awt.Dimension; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; /** A Projection_Viewer provides a view of coordinate projection mappings.

    A JFrame containing a Projection_Pane and optionally a Parameter_Pane.

    @author Bradford Castalia - UA/PIRL @version 1.5 @see PIRL.PVL.Parameter @see PIRL.Viewers.Projection_Pane @see PIRL.Viewers.Parameter_Pane */ public class Projection_Viewer extends JFrame { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Projection_Viewer (1.5 2012/04/16 06:22:59)"; private Parameter Parameters = null; private Projection_Pane Projection_Panel; private boolean Showing_Parameters; private JButton Show_Parameters_Button; private Dimension Show_Parameters_Button_Dimension; private Parameter_Pane Parameters_Panel; private int Location_Panel_Height; /** Exit status on success. */ public static final int EXIT_SUCCESS = 0; /** Exit status when a command line syntax problem occurs. */ public static final int EXIT_COMMAND_LINE_SYNTAX = 1; /** Exit status when no parameters can be obtained. */ public static final int EXIT_NO_PARAMETERS = 2; /** Exit status when no projection can be determined. */ public static final int EXIT_NO_PROJECTION = 3; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_ACTIONS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Projection_Viewer from an Aggregate Parameter.

    @param parameters The Aggregate Parameter that contains the projection parameters. @throws IllegalArgumentException If a Projection can not be constructed from the parameters. */ Projection_Viewer ( Parameter parameters ) { super ("Projection_Viewer"); if (System.getProperty ("os.name").equals ("Mac OS X")) { // Broken OS X TreeTable handles workaround. try {UIManager.setLookAndFeel (UIManager.getCrossPlatformLookAndFeelClassName ());} catch (Exception exception) {/* Just leave the existing LAF. */} } Parameters = parameters; try {Projection_Panel = new Projection_Pane (Projection.Create (parameters));} catch (Throwable throwable) { IllegalArgumentException exception = new IllegalArgumentException (ID + '\n' + "Unable to create a Projection\n" + "from the \"" + ((parameters == null) ? "null" : parameters.Name ()) + "\" parameters."); exception.initCause (throwable); throw exception; } setContentPane (Panels ()); pack (); } /*============================================================================== GUI */ private JPanel Panels () { JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Parameters name. location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 5, 0, 5); panel.add (new JLabel ("Parameters: " + ((Parameters == null) ? "none" : File_Name (Parameters.Name ()))), location); // Projection. location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (0, 0, 0, 0); panel.add (Projection_Panel, location); // Parameters. location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (0, 5, 5, 5); panel.add (Show_Parameters_Button = new JButton ("Show Parameters"), location); Showing_Parameters = false; Show_Parameters_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Show_Parameters (! Showing_Parameters);}}); Show_Parameters_Button_Dimension = Show_Parameters_Button.getPreferredSize (); String name = null; if (Parameters.Name ().equals ("-")) { Parameters.Name (Parser.CONTAINER_NAME); name = "-"; } // Don't show the parameters yet. Parameters_Panel = new Parameter_Pane (Parameters); if (name != null) Parameters.Name (name); // Size the panel to the width of the Parameters_Panel; retain the height. Dimension dimension = panel.getPreferredSize (); Location_Panel_Height = dimension.height; dimension.width = Parameters_Panel.getPreferredSize ().width; panel.setPreferredSize (dimension); return panel; } /*============================================================================== Actions */ /** Select whether the parameters used to construct the Projection are shown.

    @param show If true, a Parameter_Pane will be shown at the botton of the viewer window. If false, the Parameter_Pane will be removed from the display. */ public void Show_Parameters ( boolean show ) { if ((DEBUG & DEBUG_ACTIONS) != 0) System.out.println (">>> Show_Parameters: " + show); Showing_Parameters = show; Container container = getContentPane (); Dimension dimension = new Dimension (container.getSize ().width, Location_Panel_Height); if (Showing_Parameters) { Show_Parameters_Button.setText ("Hide Parameters"); GridBagConstraints location = new GridBagConstraints (); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; location.gridwidth = GridBagConstraints.REMAINDER; container.add (Parameters_Panel, location); dimension.height += Parameters_Panel.getPreferredSize ().height; } else { Show_Parameters_Button.setText ("Show Parameters"); container.remove (Parameters_Panel); } Show_Parameters_Button.setPreferredSize (Show_Parameters_Button_Dimension); container.setPreferredSize (dimension); // Without this pack the display will not be redrawn. pack (); if ((DEBUG & DEBUG_ACTIONS) != 0) System.out.println ("<<< Show_Parameters"); } /*============================================================================== main */ /** Launches an interactive Projection_Viewer or performs a non-interactive coordinate projection.

    Exit status values:

    {@link #EXIT_SUCCESS} (0) - Success.
  • {@link #EXIT_COMMAND_LINE_SYNTAX} (1) - A command line syntax problem occured.
  • {@link #EXIT_NO_PARAMETERS} (2) - No parameters could be obtained.
  • {@link #EXIT_NO_PROJECTION} (3) - No projection could be determined.

@param args The {@link #Usage command line} arguments. @see #Usage() */ public static void main ( String[] args ) { if (args.length == 0) Usage (); String source = null; boolean image_coordinates = false, spherical = false; Point2D.Double coordinate = null; for (int count = 0; count < args.length; count++) { if (args[count].length () == 0) continue; if (args[count].charAt (0) == '-' && args[count].length () > 1) { switch (args[count].charAt (1)) { case 'S': case 's': spherical = true; break; case 'I': // Image x,y case 'i': image_coordinates = true; case 'W': // World lon,lat case 'w': if ((count + 1) == args.length || coordinate != null) Usage (); coordinate = Coordinate (args[++count]); break; default: System.out.println ("Unknown option: " + args[count]); Usage (); } } else { if (source != null) { System.out.println ("Mulitple parameters source -\n" +" " + source + '\n' +" " + args[count] + '\n'); Usage (); } source = args[count]; } } if (source == null) { System.out.println ("No parameters source specified.\n"); Usage (); } // Assemble the source parameters. Parameter parameters = Named_Parameters (source); if (parameters == null) System.exit (EXIT_NO_PARAMETERS); // Construct a Projection from the parameters. Projection projection = null; try {projection = Projection.Create (parameters);} catch (Throwable exception) { System.out.println (exception.getMessage ()); System.exit (EXIT_NO_PROJECTION); } // Set the elliptical/spherical mode for polar projection. projection.Use_Spherical (spherical); if (coordinate == null) { // Interactive. Projection_Viewer viewer = new Projection_Viewer (parameters); viewer.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); viewer.setVisible (true); } else { // Non-interactive coordinate projection. System.out.println (" Projection: " + projection.Name ()); if (image_coordinates) { System.out.println ("Image coordinate: " + (int)coordinate.x + " x, " + (int)coordinate.y + " y"); coordinate = projection.to_World (coordinate); System.out.println ("World coordinate: " + coordinate.x + " lon, " + coordinate.y + " lat"); } else { System.out.println ("World coordinate: " + coordinate.x + " lon, " + coordinate.y + " lat"); Point image_coordinate = projection.to_Image (coordinate); System.out.println ("Image coordinate: " + image_coordinate.x + " x, " + image_coordinate.y + " y"); } System.exit (EXIT_SUCCESS); } } private static Point2D.Double Coordinate ( String coordinate ) { int index = coordinate.indexOf (','); if (index < 0) Usage (); Point2D.Double point = null; try { point = new Point2D.Double ( Double.parseDouble (coordinate.substring (0, index)), Double.parseDouble (coordinate.substring (index + 1)) ); } catch (NumberFormatException exception) { System.out.println ("Not a valid coordinate: " + coordinate); Usage (); } return point; } /** Assemble a Paremeter from a source.

If an InputStream can not be constructed on the source a message that the source could not be accessed is printed to stderr. If the source is the dash ('-') character stdin will be used. If a Parameter can not be constructed from the InputStream a message describing the problem is printed to stderr.

The name of the returned Parameter will be the source String. If a URL can not be constructed from the source String, or it a URL can be formed and it is found to have the "file" protocol, the full canonical pathname of the referenced file is set as the comment for the returned Parameter. If, however, the source String is the dash character no comment is applied.

@param source The parameter source reference String. This may be a file pathname, a URL, or the dash ('-') character to indicate that the source is stdin. @return A Parameter. This will be null if no Parameter could be constructed from the source. */ public static Parameter Named_Parameters ( String source ) { String canonical_pathname = null; InputStream source_stream; if (source.equals ("-")) source_stream = System.in; else if ((source_stream = Streams.Get_Stream (source)) != null) { // Identify the source file. try { URL url = new URL (source); if (url.getProtocol ().equals ("file")) { try {canonical_pathname = new File (url.getPath ()).getCanonicalPath ();} catch (Exception e) {} } } catch (MalformedURLException except) { try {canonical_pathname = new File (source).getCanonicalPath ();} catch (Exception e) {} } } else { System.err.println ("Unable to access source: " + source); return null; } // Load the parameters. Parameter parameters = null; try {parameters = new Parameter (source, new Parser (source_stream)) .Comments (canonical_pathname);} catch (PVL_Exception exception) { System.err.println ("Unable to parse the PVL in " + source + '\n' + exception.getMessage () + '\n'); } return parameters; } private static String File_Name ( String source ) { if (source == null || source.trim ().length () == 0 || source.equals (Parser.CONTAINER_NAME)) return ""; if (source.equals ("-")) return "stdin"; File file = null; try {file = new File (new URL (source).getPath ());} catch (MalformedURLException except) {file = new File (source);} return file.getName (); } /** Print command line usage syntax.

Usage: Projection_Viewer <Switches> <PVL source>
  Switches -
    -Spherical
    -Image <x>,<y> | -World <longitude>,<latitude>]

PVL source:

The source of PVL parameters containing the map projection information. This may be a file pathname, a URL, or the dash ('-') character to specify that stdin is the source.

Spherical:

When a polar stereographic projection is selected the faster but slightly less accurate spherical form of the projection will be used when this option is specified. Otherwise the elliptical form will be used.

Non-interactive coordinate projection:

When either an image x,y or real world longitude,latitude coordinate is specified the projection determined by the source parameters is applied to map the coordinate to the other reference system. The name of the selected projection and both sets of coordinates are printed.

N.B.This method always results in a System.exit with a status of {@link #EXIT_COMMAND_LINE_SYNTAX}. */ public static void Usage () { System.out.println ( "Usage: Projection_Viewer" + " [-Spherical]" + " [-Image , | -World ,]" + " " ); System.exit (EXIT_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Viewers/Memory_ChartUI.java0000644000175000017500000000256611742735302020245 0ustar mathieumathieu/* Memory_ChartUI PIRL CVS ID: Memory_ChartUI.java,v 1.4 2012/04/16 06:22:58 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.plaf.ComponentUI; /** A Memory_ChartUI is an empty abstract class that provides type identification for a Memory_Chart ComponentUI delegate.

@see Memory_Chart @see Memory_Chart_UI @author Bradford Castalia - idaeim @version 1.4 */ public abstract class Memory_ChartUI extends ComponentUI {} pirl-2.3.8/PIRL/Viewers/Splash_Screen.java0000644000175000017500000003131311742735303020140 0ustar mathieumathieu/* Splash_Screen PIRL CVS ID: Splash_Screen.java,v 1.6 2012/04/16 06:22:59 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JWindow; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JProgressBar; import javax.swing.Timer; import javax.swing.AbstractAction; import java.awt.event.ActionEvent; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; /** A Splash_Screen is an application start-up image window.

The splash screen image will be displayed in a borderless window centered on the display. A progress bar will be provided across the bottom of the window. The image is optional.

An optional parent window may be specified that will be displayed when the splash screen is {@link #Stop() stopped}. This is typically the application GUI.

The splash screen may be set to remain displayed for a fixed duration, or allowed to run indefinately until it is explicitly stopped.

@author Bradford Castalia, UA/PIRL @version 1.6 */ public class Splash_Screen extends JWindow { public static final String ID = "PIRL.Viewers.Splash_Screen (1.6 2012/04/16 06:22:59)"; /** Duration value for indefinate display. */ public static final int INDEFINATE_DURATION = -1; private JLabel Splash = new JLabel (); private JProgressBar Progress_Bar = new JProgressBar (); private JFrame Parent = null; private int Priority = Thread.currentThread ().getPriority (), Duration = INDEFINATE_DURATION, Elapsed = 0; private Timer Activity_Timer = new Timer (1000, new Splash_Progress ()); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_MANIPULATORS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Splash_Screen.

Note: When the splash screen image is {@link Icons#Load_Icon(String) loaded} from the image source its full pathname need not be specified if the Icons {@link Icons#Directory(String) directory} has already been specified. This is particularly useful if a {@link Icons#Directory(String, Class) class-relative directory} was specified.

@param image_source The splash screen image source. This may be a file pathname or a URL. If null, or the source is not found, the screen will be displayed without an image; only a progress bar. @param parent A JFrame window, or null. @param duration The duration of the screen display, in seconds. If negative the duration is indefinate. */ public Splash_Screen ( String image_source, JFrame parent, int duration ) { super (parent); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> Splash_Screen:\n" +" image_source - " + image_source + '\n' +" duration - " + duration); Parent = parent; getContentPane ().setLayout (new BorderLayout ()); ImageIcon image = Icons.Load_Icon (image_source); if (image != null) { Splash.setIcon (image); getContentPane ().add (Splash); } Progress_Bar.setStringPainted (true); if ((Duration = duration) < 0) { Duration = INDEFINATE_DURATION; Progress_Bar.setIndeterminate (true); Progress_Bar.setString (""); } else { Progress_Bar.setMaximum (Duration); Progress_Bar.setIndeterminate (false); Progress_Bar.setString (null); } getContentPane ().add (Progress_Bar, BorderLayout.SOUTH); pack (); Dimension window_size = getPreferredSize (), screen_size = Toolkit.getDefaultToolkit ().getScreenSize (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" screen size = " + screen_size + '\n' +" window size = " + window_size + '\n' +" location = " + ((screen_size.width / 2) - (window_size.width / 2)) + ',' + ((screen_size.height / 2) - (window_size.height / 2))); setLocation ((screen_size.width / 2) - (window_size.width / 2), (screen_size.height / 2) - (window_size.height / 2)); // Every second counts. Activity_Timer.setCoalesce (false); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< Splash_Screen"); } /** Construct an indefinate duration Splash_Screen with a parent window.

@param image_source The splash screen image source. This may be a file pathname or a URL. If null, or the source is not found, the screen will be displayed without an image; only a progress bar. @param parent A JFrame window, or null. */ public Splash_Screen ( String image_source, JFrame parent ) {this (image_source, parent, -1);} /** Construct a Splash_Screen.

@param image_source The splash screen image source. This may be a file pathname or a URL. If null, or the source is not found, the screen will be displayed without an image; only a progress bar. @param duration The duration of the screen display, in seconds. If negative the duration is indefinate. */ public Splash_Screen ( String image_source, int duration ) {this (image_source, null, duration);} /** Construct an indefinate duration Splash_Screen.

@param image_source The splash screen image source. This may be a file pathname or a URL. If null, or the source is not found, the screen will be displayed without an image; only a progress bar. */ public Splash_Screen ( String image_source ) {this (image_source, null, -1);} /** Construct an indefinate duration Splash_Screen.

The splash screen display will only have a progress bar. */ public Splash_Screen () {this (null, null, -1);} /*============================================================================== Accessors */ /** Set the parent window.

If the parent is non-null it will be displayed when the splash is {@link #Stop() stopped}.

@param parent A JFrame window, or null. @return This Splash_Screen */ public Splash_Screen Parent ( JFrame parent ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Splash_Screen.Parent"); Parent = parent; return this; } /** Get the parent window.

@return The parent JFrame window, or null. */ public JFrame Parent () {return Parent;} /** Set the splash screen image.

If the splash screen is currently being displayed, it is {@link #Stop() stopped} (but the parent window, if any, is first saved, set to null, and then restored after the image has been set).

Note: When the splash screen image is {@link Icons#Load_Icon(String) loaded} from the image source its full pathname need not be specified if the Icons {@link Icons#Directory(String) directory} has already been specified. This is particularly useful if a {@link Icons#Directory(String, Class) class-relative directory} was specified.

@param image_source The splash screen image source. This may be a file pathname or a URL. If null, or the source is not found, the screen will be displayed without an image; only a progress bar. @see Icons#Load_Icon(String) */ public Splash_Screen Image ( String image_source ) { JFrame parent = Parent; Parent = null; Stop (); ImageIcon image = Icons.Load_Icon (image_source); if (image != null) { if (Splash.getIcon () == null) getContentPane ().add (Splash, 0); } else { if (Splash.getIcon () != null) getContentPane ().remove (0); } Parent = parent; Splash.setIcon (image); validate (); pack (); Dimension window_size = getPreferredSize (), screen_size = Toolkit.getDefaultToolkit ().getScreenSize (); setLocation ((screen_size.width / 2) - (window_size.width / 2), (screen_size.height / 2) - (window_size.height / 2)); return this; } /** Set the duration of the screen display.

If the duration is negative the screen will be displayed indefinately. In this case an indeterminate progress bar will be used. Otherwise a progress bar displaying the percent progress to the duration will be used.

@param duration The duration of the screen display, in seconds. If negative the duration is indefinate. @return This Splash_Screen. */ public Splash_Screen Duration ( int duration ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Splash_Screen.Duration: " + duration); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Duration = " + Duration); if (duration < 0) duration = INDEFINATE_DURATION; if (Progress_Bar.isIndeterminate ()) { if (duration >= 0) { // Change from indeterminate to determinate. if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Change from indeterminate to determinate"); Progress_Bar.setMaximum (Duration = duration); Progress_Bar.setValue (Math.min (duration, Elapsed)); Progress_Bar.setIndeterminate (false); Progress_Bar.setString (null); } } else { if ((Duration = duration) < 0) { // Change from determinate to indeterminate. if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Change from determinate to indeterminate"); Progress_Bar.setIndeterminate (true); Progress_Bar.setString (""); } } if (Activity_Timer.getDelay () == 500) { --Elapsed; Activity_Timer.setDelay (1000); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Splash_Screen.Duration: " + Duration); return this; } /** Get the screen display duration.

@return The duration (in seconds) of the screen display. If negative, the screen duration is indefinate. */ public int Duration () {return Duration;} /** Get the screen display elapsed time.

@return The elapsed time (in seconds) since the screen display was started. This will be zero if the screen is not being displayed or has just started being displayed. */ public int Elapsed () {return Elapsed;} /*============================================================================== Manipulators */ /** Start the splash screen display.

The current thread priority is saved and then set to the maximum priority. The splash screen is then displayed. If the screen is running for a set {@link #Duration(int) duration} a timer is started.

@see #Stop() */ public Splash_Screen Start () { if (! Activity_Timer.isRunning ()) { Priority = Thread.currentThread ().getPriority (); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">>> Splash_Screen.Start\n" +" current thread group max priority: " + Thread.currentThread ().getThreadGroup ().getMaxPriority () + '\n' +" current thread priority: " + Priority + '\n' +" new thread priority: " + Thread.MAX_PRIORITY); Thread.currentThread ().setPriority (Thread.MAX_PRIORITY); setVisible (true); Activity_Timer.setDelay (1000); Activity_Timer.start (); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println ("<<< Splash_Screen.Start"); } return this; } /** Stop the splash screen.

If the screen is running for a set {@link #Duration(int) duration} the timer is stopped. The screen is removed from the display and the current thread's priority is restored to its intitial value. If a non-null {@link #Parent(JFrame) parent window} has been bound to this splash screen it is displayed.

@see #Start() */ public void Stop () { if (Activity_Timer.isRunning ()) { if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">>> Splash_Screen.Stop"); Activity_Timer.stop (); Activity_Timer.setDelay (1000); Elapsed = 0; setVisible (false); Thread.currentThread ().setPriority (Priority); if (Parent != null) Parent.setVisible (true); if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println ("<<< Splash_Screen.Stop"); } } /** Splash screen progress Action attached to the Activity_Timer. */ private class Splash_Progress extends AbstractAction { public void actionPerformed (ActionEvent event) { ++Elapsed; if ((DEBUG & DEBUG_MANIPULATORS) != 0) System.out.println (">-< Splash_Screen.Activity_Timer: Elapsed = " + Elapsed); if (Duration >= 0) { if (Elapsed <= Duration) { Progress_Bar.setValue (Elapsed); if (Elapsed == Duration) Activity_Timer.setDelay (500); } else Stop (); } } } // End of Splash_Progress class. } pirl-2.3.8/PIRL/Viewers/Makefile0000644000175000017500000000174711033021644016201 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.22 2008/07/03 00:56:04 castalia Exp # gmake syntax JC ?= javac JPATH ?= ../.. .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Blinker.class \ Dialog_Box.class \ URL_Dialog.class \ Memory_Panel.class \ Parameter_Pane.class \ Parameter_View.class \ Value_Pane.class \ Value_View.class \ View_Locator.class \ Font_Selector.class \ Icons.class \ Splash_Screen.class \ Draggable_Rows.class \ Projection_Pane.class \ Projection_Viewer.class \ Memory_Chart.class \ Memory_History.class \ Memory_ChartUI.class \ Memory_Chart_UI.class \ Memory_Chart_Panel.class \ Stream_Monitor.class \ Stream_Viewer.class \ Animator.class \ Percent_Bar.class \ Percent_BarUI.class \ Percent_Bar_UI.class \ Password_Dialog.class \ Size_Limits.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Viewers/Dialog_Box.java0000644000175000017500000002236711742735302017426 0ustar mathieumathieu/* Dialog_Box PIRL CVS ID: Dialog_Box.java,v 1.12 2012/04/16 06:22:58 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JOptionPane; import java.awt.Component; import java.awt.Frame; import java.awt.Toolkit; /** The Dialog_Box class provides a collection of static methods that are convenient for producing commonly used Dialog_Boxes.

@author Bradford Castalia, UA/PIRL @version 1.12 */ public class Dialog_Box extends JOptionPane { private static final String ID = "PIRL.Viewers.Dialog_Box (1.12 2012/04/16 06:22:58)"; /** The maximum number of characters per line in a dialog box. A width <= 0 is an unlimited width. */ public static int MAX_LINE_LENGTH = 70; // For beep. private static final Toolkit TOOLKIT = Toolkit.getDefaultToolkit (); // For Fit_to_Width. private static final String NL = System.getProperty ("line.separator"); /*============================================================================== Constructors */ // This class only contains static functions. private Dialog_Box () {} /*============================================================================== Functions */ /** Displays an INFORMATION_MESSAGE MessageDialog box with a "Notice" title and an OK button.

@param message A String message that will be displayed in the dialog box. @param parent The parent Component relative to which the dialog box will be positioned. This may be null in which case the Dialog_Box will be centered on the display. */ public static void Notice ( String message, Component parent ) { showMessageDialog ( parent, Fit_to_Width (message), "Notice", INFORMATION_MESSAGE ); } /** Displays an INFORMATION_MESSAGE MessageDialog box centered on the screen with a "Notice" title and an OK button.

@param message A String message that will be displayed in the dialog box. */ public static void Notice (String message) {Notice (message, null);} /** Displays a WARNING_MESSAGE MessageDialog box, preceeded by an audible beep, with a "Warning" title and an OK button.

@param message A String message that will be displayed in the dialog box. @param parent The parent Component relative to which the dialog box will be positioned. This may be null in which case the Dialog_Box will be centered on the display. */ public static void Warning ( String message, Component parent ) { TOOLKIT.beep (); showMessageDialog ( parent, Fit_to_Width (message), "Warning", WARNING_MESSAGE ); } /** Displays a WARNING_MESSAGE MessageDialog box centered on the screen, preceeded by an audible beep, with a "Warning" title and an OK button.

@param message A String message that will be displayed in the dialog box. */ public static void Warning (String message) {Warning (message, null);} /** Displays an ERROR_MESSAGE MessageDialog box, preceeded by an audible beep, with an "Error" title and an OK button.

@param message A String message that will be displayed in the dialog box. @param parent The parent Component relative to which the dialog box will be positioned. This may be null in which case the Dialog_Box will be centered on the display. */ public static void Error ( String message, Component parent ) { TOOLKIT.beep (); showMessageDialog ( parent, Fit_to_Width (message), "Error", ERROR_MESSAGE ); } /** Displays an ERROR_MESSAGE MessageDialog box centered on the screen, preceeded by an audible beep, with an "Error" title and an OK button.

@param message A String message that will be displayed in the dialog box. */ public static void Error (String message) {Error (message, null);} /** Displays a ConfirmDialog box with a "Confirm" title and an OK and Cancel button.

@param message A String message that will be displayed in the dialog box. @param parent The parent Component relative to which the dialog box will be positioned. This may be null in which case the Dialog_Box will be centered on the display. @return true if the OK button was pressed; false otherwise. */ public static boolean Confirm ( String message, Component parent ) { return showConfirmDialog ( parent, Fit_to_Width (message), "Confirm", OK_CANCEL_OPTION ) == OK_OPTION; } /** Displays a ConfirmDialog box centered on the screen with a "Confirm" title and an OK and Cancel button.

@param message A String message that will be displayed in the dialog box. @return true if the OK button was pressed; false otherwise. */ public static boolean Confirm (String message) {return Confirm (message, null);} /** Displays a ConfirmDialog box with a "Check" title and a Yes, No and Cancel button.

@param message A String message that will be displayed in the dialog box. @param parent The parent Component relative to which the dialog box will be positioned. This may be null in which case the Dialog_Box will be centered on the display. @return 1 if the Yes button was pressed, -1 if the No button was pressed, or 0 if the Cancel button was pressed. */ public static int Check ( String message, Component parent ) { int check = showConfirmDialog ( parent, Fit_to_Width (message), "Check", YES_NO_CANCEL_OPTION ); switch (check) { case YES_OPTION: return 1; case NO_OPTION: return -1; default: return 0; } } /** Displays a ConfirmDialog box centered on the screen with a "Check" title and a Yes, No and Cancel button.

@param message A String message that will be displayed in the dialog box. @return 1 if the Yes button was pressed, -1 if the No button was pressed, or 0 if the Cancel button was pressed. */ public static int Check (String message) {return Check (message, null);} /** Displays a Password_Dialog with a password input field preceded by a prompt.

@param prompt The text preceding the password input field. If null, "Password" will be used by default. @param parent The parent Frame relative to which the dialog box will be positioned. This may be null. */ public static String Password ( String prompt, Frame parent ) { Password_Dialog password_dialog = new Password_Dialog ("Password", prompt, parent); password_dialog.setVisible (true); String password_string = null; char[] password = password_dialog.Password (); if (password != null) { password_string = new String (password); password_dialog.Erase_Password (); } return password_string; } /** Displays a Password_Dialog with a password input field preceded by a prompt.

@param prompt The text preceding the password input field. If null, "Password" will be used by default. */ public static String Password ( String prompt ) {return Password (prompt, null);} /*============================================================================== Helpers */ /** Fits (wraps using NL sequences) a String to a given width.

A NL replaces the last whitespace sequence before the width of the current line is reached. If there is no whitespace, then the NL is inserted at the width. A width <= 0 is an unlimited width. */ public static String Fit_to_Width ( String in_string, int width ) { if (in_string == null) return "null"; int total = in_string.length (); if (width >= total || width <= 0) return in_string; StringBuffer out_string = new StringBuffer (in_string); int NL_length = NL.length (), count = 0, column = 0, break_column = 0; while (column < total) { if ((column + NL_length) < total && out_string.substring (column, column + NL_length).equals (NL)) { // Line break. column += NL_length; count = break_column = 0; } else if (count < width) { if (out_string.charAt (column) == ' ') break_column = column; column++; count++; } else { // Wrap the long line. while (break_column > 0 && out_string.charAt (break_column) == ' ') { // Delete the break space(s). out_string.delete (break_column, break_column + 1); break_column--; column--; total--; } if (break_column == 0) break_column = column; else column = ++break_column; if (column != 0) { // Insert a line break. out_string.insert (column, NL); column += NL_length; total += NL_length; } count = break_column = 0; } } return out_string.toString (); } /** Fits a String to the MAX_LINE_LENGTH.

@param string The String to be fitted. @return The fitted String. @see #Fit_to_Width(String, int) */ public static String Fit_to_Width (String string) {return Fit_to_Width (string, MAX_LINE_LENGTH);} } // End of Dialog_Box class. pirl-2.3.8/PIRL/Viewers/Projection_Pane.java0000644000175000017500000002250311742735303020467 0ustar mathieumathieu/* Projection_Pane PIRL CVS ID: Projection_Pane.java,v 1.4 2012/04/16 06:22:59 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import PIRL.Image_Tools.Projection; import PIRL.PVL.Parameter; import PIRL.PVL.Parser; import PIRL.PVL.PVL_Exception; import PIRL.Viewers.Dialog_Box; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JTextField; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.Point; import java.awt.geom.Point2D; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.NoSuchElementException; /** A Projection_Pane provides a JPanel with interactive image and real world coordinate fields.

The Projection used to construct the Projection_Pane is used to map between image and world coordinates. Whenever a new coordinate value is entered the other coordinate values are updated.

@author Bradford Castalia UA/PIRL @version 1.4 @see PIRL.Image_Tools.Projection */ public class Projection_Pane extends JPanel { /** Class name and version identification. */ public static final String ID = "PIRL.Image_Tools.Projection_Pane (1.4 2012/04/16 06:22:59)"; private Parameter Parameters = null; private Projection The_Projection = new Projection (); private JTextField Image_X_Field, Image_Y_Field, World_Latitude_Field, World_Longitude_Field; private Point Image_Coordinates = new Point (0, 0); private Point2D.Double World_Coordinates; public static final int EXIT_SUCCESS = 0, EXIT_COMMAND_LINE_SYNTAX = 1, EXIT_NO_PARAMETERS = 2, EXIT_NO_PROJECTION = 3; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_CONVERTERS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Projection_Pane from a Projection.

@param projection The Projection to use. */ Projection_Pane ( Projection projection ) { if (projection == null) projection = new Projection (); The_Projection = projection; Assemble_Panel (); } /** Construct a Pojrection_Pane with an identity Projection. */ Projection_Pane () {this (null);} /*============================================================================== GUI */ private void Assemble_Panel () { setLayout (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Projection name. location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (5, 5, 0, 5); add (new JLabel ("Projection:"), location); location.anchor = GridBagConstraints.WEST; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 0, 0, 5); add (new JLabel (The_Projection.Name ()), location); // Image coordinates. location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.gridwidth = 1; location.insets = new Insets (5, 5, 0, 5); add (new JLabel ("Image:"), location); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 0.5; location.insets = new Insets (5, 0, 0, 5); add (Image_X_Field = new JTextField ("0"), location); Image_X_Field.setHorizontalAlignment (JTextField.RIGHT); Image_X_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Image_X_Action ();}}); location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.insets = new Insets (5, 0, 0, 10); add (new JLabel ("x"), location); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 0.5; location.insets = new Insets (5, 0, 0, 5); add (Image_Y_Field = new JTextField ("0"), location); Image_Y_Field.setHorizontalAlignment (JTextField.RIGHT); Image_Y_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Image_Y_Action ();}}); location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (5, 0, 0, 5); add (new JLabel ("y"), location); // World coordinates. location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.gridwidth = 1; location.insets = new Insets (0, 5, 5, 5); add (new JLabel ("World:"), location); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 0.5; location.insets = new Insets (0, 0, 5, 5); add (World_Longitude_Field = new JTextField (7), location); World_Longitude_Field.setHorizontalAlignment (JTextField.RIGHT); World_Longitude_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {World_Longitude_Action ();}}); location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.insets = new Insets (0, 0, 5, 10); add (new JLabel ("lon"), location); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 0.5; location.insets = new Insets (0, 0, 5, 5); add (World_Latitude_Field = new JTextField (7), location); World_Latitude_Field.setHorizontalAlignment (JTextField.RIGHT); World_Latitude_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {World_Latitude_Action ();}}); location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.gridwidth = GridBagConstraints.REMAINDER; location.insets = new Insets (0, 0, 5, 5); add (new JLabel ("lat"), location); Image_X_Action (); // Initialize the fields. } /*============================================================================== Actions */ private void Image_X_Action () { String value = Image_X_Field.getText ().trim (); if (value.length () == 0) Image_X_Field.setText (String.valueOf (Image_Coordinates.x)); else { try { Image_Coordinates.x = Integer.parseInt (value); World_Coordinates = The_Projection.to_World (Image_Coordinates); World_Longitude_Field.setText (String.valueOf (World_Coordinates.x)); World_Latitude_Field.setText (String.valueOf (World_Coordinates.y)); } catch (NumberFormatException exception) { Dialog_Box.Error ("An image x coordinate is required.\n" +'"' + value + "\" is not an integer.", this); Image_X_Field.setText (String.valueOf (Image_Coordinates.x)); } } } private void Image_Y_Action () { String value = Image_Y_Field.getText ().trim (); if (value.length () == 0) Image_Y_Field.setText (String.valueOf (Image_Coordinates.y)); else { try { Image_Coordinates.y = Integer.parseInt (value); World_Coordinates = The_Projection.to_World (Image_Coordinates); World_Longitude_Field.setText (String.valueOf (World_Coordinates.x)); World_Latitude_Field.setText (String.valueOf (World_Coordinates.y)); } catch (NumberFormatException exception) { Dialog_Box.Error ("An image y coordinate is required.\n" +'"' + value + "\" is not an integer.", this); Image_Y_Field.setText (String.valueOf (Image_Coordinates.y)); } } } private void World_Longitude_Action () { String value = World_Longitude_Field.getText ().trim (); if (value.length () == 0) World_Longitude_Field.setText (String.valueOf (World_Coordinates.x)); else { try { World_Coordinates.x = Double.parseDouble (value); Image_Coordinates = The_Projection.to_Image (World_Coordinates); Image_X_Field.setText (String.valueOf (Image_Coordinates.x)); Image_Y_Field.setText (String.valueOf (Image_Coordinates.y)); } catch (NumberFormatException exception) { Dialog_Box.Error ("A longitude value is required.\n" +'"' + value + "\" is not a real number.", this); World_Longitude_Field.setText (String.valueOf (World_Coordinates.x)); } } } private void World_Latitude_Action () { String value = World_Latitude_Field.getText ().trim (); if (value.length () == 0) World_Latitude_Field.setText (String.valueOf (World_Coordinates.y)); else { try { World_Coordinates.y = Double.parseDouble (value); Image_Coordinates = The_Projection.to_Image (World_Coordinates); Image_X_Field.setText (String.valueOf (Image_Coordinates.x)); Image_Y_Field.setText (String.valueOf (Image_Coordinates.y)); } catch (NumberFormatException exception) { Dialog_Box.Error ("A latitude value is required.\n" +'"' + value + "\" is not a real number.", this); World_Latitude_Field.setText (String.valueOf (World_Coordinates.y)); } } } } pirl-2.3.8/PIRL/Viewers/package.html0000644000175000017500000000274011742735303017027 0ustar mathieumathieu The PIRL Viewers package provides classes for common capabilies associated with GUI viewers. These are offered as a convenience when producing Viewers.

Viewers are provided for working with PVL Parameters and Values, Projection Parameters, displaying an application startup splash screen, managing window locations, generating Dialog boxes, loading Icons, selecting fonts, blinking a component background color, assembling a draggable stack of components, and monitoring memory usage. pirl-2.3.8/PIRL/Viewers/Memory_Panel.java0000644000175000017500000002176611742735303020011 0ustar mathieumathieu/* Memory_Panel PIRL CVS ID: Memory_Panel.java,v 1.5 2012/04/16 06:22:59 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.text.DecimalFormat; import javax.swing.JLabel; import javax.swing.JPanel; /** A {@link JPanel} that holds information about available Virtual Machine memory. Three values are displayed: free memory, total memory, and maximum memory.

Free memory is the amount of memory not yet in use by the Virtual Machine. This memory has been claimed from the available pool, however.

Total memory is the total amount of memory the Virtual Machine has claimed from the available pool of memory. Free memory is a subset of this quantity; when subtracted from this quantity, the difference is the amount of memory currently in use. This value will change over time as objects are created and destroyed.

Maximum memory is the maximum available pool of memory for the Virtual Machine. Its value is static once the Virtual Machine is instantiated, although runtime switches may be employed to allocate amounts other than the default; see the documentation for the Java Virtual Machine. Total memory is a subset of this quantity.

@author Christian Schaller - UA/PIRL @version 1.5 */ public class Memory_Panel extends JPanel { /*============================================================================== Constants */ /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Memory_Panel (1.5 2012/04/16 06:22:59)"; /** The default unit divisor. Memory quantities will, by default, be divided by this value for display in the panel. This value is the number of bytes in a megabyte (1024 * 1024). */ public static final int DEFAULT_UNIT_DIVISOR = 1024 * 1024; /** The default unit. Memory quantities will be displayed with this text in the panel. */ public static final String DEFAULT_UNITS = "MB"; /*============================================================================== Fields */ /** The unit divisor for all memory quantities. */ private int unit_divisor; /** The units for all memory quantities. */ private String units; /*============================================================================== Constructors */ /*.............................................................................. Memory_Panel (int, String) */ /** Creates a new Memory_Panel with the specified unit_divisor and units label. The divisor is used to scale the memory values, which are in bytes; the units label should correctly identify the scale.

This panel uses a {@link GridBagLayout}.

@param unit_divisor the divisor to be used in scaling the displayed memory values. @param units the label that identifies the units used. */ public Memory_Panel ( int unit_divisor, String units ) { super (new GridBagLayout ()); this.unit_divisor = unit_divisor; this.units = units; build_ui (); } /*.............................................................................. Memory_Panel () */ /** Creates a new Memory_Panel. The panel uses the {@link #DEFAULT_UNIT_DIVISOR default divisor} and {@link #DEFAULT_UNITS default units} for its information display. @see #Memory_Panel(int,String) */ public Memory_Panel () { this (DEFAULT_UNIT_DIVISOR, DEFAULT_UNITS); } /*============================================================================== User interface */ private JLabel free_label, total_label, max_label; private DecimalFormat formatter; /*.............................................................................. build_ui () */ /** Builds the user interface. The panel is initialized and its layout set (to a {@link GridBagLayout}). The labels are instantiated and placed into the layout. */ private void build_ui () { this.formatter = new DecimalFormat ("#,##0.00"); this.free_label = new JLabel ("", JLabel.TRAILING); this.total_label = new JLabel ("", JLabel.TRAILING); this.max_label = new JLabel ("", JLabel.TRAILING); update_labels (); GridBagConstraints constraints = new GridBagConstraints (); constraints.gridx = 0; constraints.gridy = 0; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.insets = new Insets (0,0,0,5); this.add (new JLabel ("Free Memory:"), constraints); constraints.gridy++; this.add (new JLabel ("Total Memory:"), constraints); constraints.gridy++; this.add (new JLabel ("Maximum Memory:"), constraints); constraints.gridx++; constraints.gridy = 0; constraints.insets = new Insets (0,5,0,0); this.add (free_label, constraints); constraints.gridy++; this.add (total_label, constraints); constraints.gridy++; this.add (max_label, constraints); } /*.............................................................................. update_labels () */ /** Updates the memory labels. This update takes place automatically whenever the units or unit divisors are changed, but one might want to update the labels periodically, polling the system from time to time. */ public void update_labels () { free_label.setText ( formatter.format (free_memory () / unit_divisor) + " " + units ); total_label.setText ( formatter.format (total_memory () / unit_divisor) + " " + units ); max_label.setText ( formatter.format (max_memory () / unit_divisor) + " " + units ); } /*============================================================================== Accessors */ /*.............................................................................. unit_divisor () */ /** Gets the common divisor used for each of the three memory quantities. Each quantity is divided by this value for display. @return the unit divisor for this Memory_Panel. */ public int unit_divisor () { return this.unit_divisor; } /*.............................................................................. unit_divisor (int) */ /** Sets the common divisor used for each of the three memory quantities. Each quantity is divided by this value or display. The displayed labels are updated when this value is changed. @param unit_divisor the new divisor for this Memory_Panel. */ public void unit_divisor ( int unit_divisor ) { this.unit_divisor = unit_divisor; update_labels (); } /*.............................................................................. units () */ /** Gets the units displayed for each of the three memory quantities. @return the units for this Memory_Panel. */ public String units () { return this.units; } /*.............................................................................. units (String) */ /** Sets the units displayued for each of the three memory quantities. The displayed labels are updated when this value is changed. @param units the new units for this Memory_Panel. */ public void units ( String units ) { this.units = units; update_labels (); } /*============================================================================== Static convenience methods */ /*.............................................................................. free_memory () */ /** Returns the amount of free memory currently available to the Java Virtual Machine. @return the free memory in bytes. @see Runtime#freeMemory */ public static long free_memory () { return Runtime.getRuntime ().freeMemory (); } /*.............................................................................. total_memory () */ /** Returns the total amount of memory in the Java Virtual Machine. This value may vary over time as the Virtual Machine claims more memory from the maximum available and releases it. @return the total memory in bytes. @see Runtime#totalMemory */ public static long total_memory () { return Runtime.getRuntime ().totalMemory (); } /*.............................................................................. max_memory () */ /** Returns the maximum amount of memory the Java Virtual Machine will attempt to use from the host system. @return the maximum memory in bytes. @see Runtime#maxMemory */ public static long max_memory () { return Runtime.getRuntime ().maxMemory (); } } // End of Memory_Panel class. pirl-2.3.8/PIRL/Viewers/Memory_Chart_UI.java0000644000175000017500000002274611742735302020406 0ustar mathieumathieu/* Memory_Chart_UI PIRL CVS ID: Memory_Chart_UI.java,v 1.5 2012/04/16 06:22:58 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Viewers; import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; import java.awt.Color; import java.awt.Insets; import java.awt.Graphics; import java.awt.FontMetrics; import java.awt.geom.Rectangle2D; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** A Memory_Chart_UI provides a ComponentUI delegate for the Memory_Chart component.

@see Memory_Chart @version 1.5 @see Memory_History */ public class Memory_Chart_UI extends Memory_ChartUI { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Viewers.Memory_Chart_UI (1.5 2012/04/16 06:22:58)"; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_INSTALL = 1 << 1, DEBUG_PAINT = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Memory_Chart_UI object. */ public Memory_Chart_UI () { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Memory_Chart_UI.Constructor"); } /** Construct a Memory_Chart_UI object for the specified JComponent.

@param component The JComponent for which ComponentUI delegation is to be provided. @return A Memory_Chart_UI object. */ public static ComponentUI createUI ( JComponent component ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Memory_Chart_UI.createUI"); return new Memory_Chart_UI (); } /*============================================================================== Component Painting */ /** Paint the component on the graphics display device.

@param graphics The Graphics on which to paint the component. @param component The JComponent to be painted. */ public void paint ( Graphics graphics, JComponent component ) { if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (">>> Memory_Chart_UI.paint"); Memory_Chart chart = (Memory_Chart)component; Memory_History history = chart.getModel (); if (history.Samples == 0) return; // Geometry. Insets insets = component.getInsets (); int canvas_width = chart.getWidth () - insets.left - insets.right, canvas_height = chart.getHeight () - insets.top - insets.bottom, annotation_area_width = chart.Annotation_Area_Width (), annotation_text_height = chart.Annotation_Text_Height (), seconds_scale_height = chart.Seconds_Scale_Height (), chart_width = canvas_width - annotation_area_width, chart_height = canvas_height - seconds_scale_height; if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (" canvas_width = " + canvas_width + '\n' +" canvas_height = " + canvas_height + '\n' +" annotation_area_width = " + annotation_area_width + '\n' +" annotation_text_height = " + annotation_text_height + '\n' +" seconds_scale_height = " + seconds_scale_height + '\n' +" chart_width = " + chart_width + '\n' +" chart_height = " + chart_height); if (chart_width < 5 || chart_height < (seconds_scale_height + 10)) { // Not enough area to draw a meaningful chart. if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (" Insufficient chart area.\n" +"<<< Memory_Chart_UI.paint"); return; } if (chart_width > history.Max_Samples ()) { if ((DEBUG & DEBUG_PAINT) != 0) System.out.println (" Increasing Memory_History Max_Samples to " + chart_width); // Add more capacity to the memory history. history.Max_Samples (chart_width); // The model will fire a change event causing a repaint. if ((DEBUG & DEBUG_PAINT) != 0) System.out.println ("<<< Memory_Chart_UI.paint"); return; } long available_memory = history.Available_Memory (); // Set 0,0 at the upper-left corner of the chart. graphics.translate (insets.left, insets.top); // Font to use for annotation and seconds scale. graphics.setFont (chart.ANNOTATION_FONT); FontMetrics font_metrics = chart.getFontMetrics (chart.ANNOTATION_FONT); String annotation; Rectangle2D bounds; // Annotation. if (annotation_area_width > 0 && (annotation_text_height << 2) <= chart_height) { // Total available. annotation = Memory_Magnitude (available_memory); bounds = font_metrics.getStringBounds (annotation, graphics); graphics.setColor (Color.BLACK); graphics.drawString (annotation, canvas_width - (int)bounds.getWidth (), annotation_text_height - 1); // Unused. annotation = Memory_Magnitude (available_memory - history.Allocated[0]); bounds = font_metrics.getStringBounds (annotation, graphics); graphics.setColor (Color.BLUE); graphics.drawString (annotation, canvas_width - (int)bounds.getWidth (), annotation_text_height << 1); // Allocated but free. annotation = Memory_Magnitude (history.Free[0]); bounds = font_metrics.getStringBounds (annotation, graphics); graphics.setColor (Color.GREEN); graphics.drawString (annotation, canvas_width - (int)bounds.getWidth (), chart_height - annotation_text_height); // In use. annotation = Memory_Magnitude (history.Allocated[0] - history.Free[0]); bounds = font_metrics.getStringBounds (annotation, graphics); graphics.setColor (Color.RED); graphics.drawString (annotation, canvas_width - (int)bounds.getWidth (), chart_height); } int rate = history.Rate (); if (rate == 0) rate = history.Previous_Rate (); if (rate > 0 && seconds_scale_height > 0) { // Seconds scale annotation. if (rate > 1) annotation = "x" + String.valueOf (rate) + " s"; else annotation = "secs"; graphics.setColor (Color.BLACK); graphics.drawString (annotation, chart_width + 2, canvas_height - 1); } // Chart. int sample, column = chart_width, bottom, top; for (sample = 0; sample < history.Samples && sample < chart_width; ++sample) { --column; if (seconds_scale_height > 0) { // Seconds scale. if ((sample % 10) == 0) { // Major tick mark. graphics.setColor (Color.BLACK); graphics.drawLine (column, chart_height, column, canvas_height - 1); if (sample != 0 && rate < 20 && ((sample * rate) % 60) == 0) { // Minute tick mark. graphics.drawLine (column - 1, chart_height, column - 1, canvas_height - 1); graphics.drawLine (column + 1, chart_height, column + 1, canvas_height - 1); } } else if ((sample % 5) == 0) { // Minor tick mark. graphics.setColor (Color.BLACK); graphics.drawLine (column, chart_height, column, chart_height + (seconds_scale_height >> 1)); } } bottom = chart_height - 1; if (history.Allocated[sample] < 0) { // Division bar. graphics.setColor (Color.BLACK); graphics.drawLine (column, 0, column, chart_height - 1); continue; } if (history.Allocated[sample] > 0) { top = chart_height - 1 -(int)((((double)(history.Allocated[sample] - history.Free[sample]) / available_memory) * chart_height) + 0.5); if (top < bottom) { graphics.setColor (Color.RED); graphics.drawLine (column, bottom, column, top); bottom = top - 1; } if (history.Free[sample] > 0) { top = chart_height - 1 - (int)((((double)history.Allocated[sample] / available_memory) * chart_height) + 0.5); if (top < bottom) { graphics.setColor (Color.GREEN); graphics.drawLine (column, bottom, column, top); bottom = top - 1; } } } if (bottom >= 0) { graphics.setColor (Color.BLUE); graphics.drawLine (column, bottom, column, 0); } } if (seconds_scale_height > 0) { // Max history time. annotation = String.valueOf (rate * sample); bounds = font_metrics.getStringBounds (annotation, graphics); int width = (int)bounds.getWidth () + 4; if (width <= sample) { graphics.setColor (Color.WHITE); graphics.fillRect (column, chart_height, width, seconds_scale_height); graphics.setColor (Color.BLACK); graphics.drawLine (column, chart_height, column, canvas_height - 1); graphics.drawString (annotation, column + 2, canvas_height); } } // Baseline. graphics.setColor (Color.BLACK); graphics.drawLine (column, chart_height, chart_width - 1, chart_height); // Restore the graphics origin. graphics.translate (-insets.left, -insets.top); if ((DEBUG & DEBUG_PAINT) != 0) System.out.println ("<<< Memory_Chart_UI.paint"); } //! Available memory amount magnitude indicator. private static final String Magnitude[] = {" ", "K", "M", "G", "T", "P"}; private String Memory_Magnitude ( long value ) { int magnitude; for (magnitude = 0; value >= 1024; magnitude++, value >>>= 10); return String.valueOf (value) + Magnitude[magnitude]; } } // End of Memory_Chart_UI class. pirl-2.3.8/PIRL/Strings/0000755000175000017500000000000012052546515014550 5ustar mathieumathieupirl-2.3.8/PIRL/Strings/String_Buffer_Reader.java0000644000175000017500000020264711742734407021453 0ustar mathieumathieu/* String_Buffer_Reader PIRL CVS ID: String_Buffer_Reader.java,v 1.16 2012/04/16 06:15:35 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Strings; import java.lang.StringBuffer; import java.lang.String; import java.lang.Math; import java.lang.IllegalStateException; import java.io.Reader; import java.io.IOException; import java.text.CharacterIterator; /** A String_Buffer_Reader provides methods to manipulate a character stream as if it were a String_Buffer by backing it with a Reader to provide the characters for the String_Buffer.

The intended use is to consume a character stream, in a semi-sequential manner, processing statements as they are encountered by moving along the stream from statement to statement. A statement is composed of a contiguous sequence of characters of variable length.

The character stream may be sourced from a Reader or a String. A String source is a convenience that allows a String_Buffer to be used transparently in the same context where a Reader might also be used, but instead a "pre-read" source of characters is provided directly (a StringReader could also be used).

For a Reader source the character stream is buffered using a sliding window. The source contents are referenced by virtual Location values that act as indexes into the entire character stream; the first character of the stream is at location 0. The buffer window is automatically extended to contain characters from any location available in the input stream.

To avoid a large number of read operations on the input stream, the buffer is always extended by a specified Size_Increment. In addition, a Read_Limit may be specified to force input termination at any location in the character stream. Input may also be terminated when a threshold of non-text bytes (more than a specified sequential limit) occur in the stream, as would typically occur after the initial label area of an image file.

When statements from the character stream have been processed and are no longer needed the Next_Location is updated. That part of the character stream in the buffer before the Next_Location is considered to be disposable so that the next time the buffer needs to be extended to a new stream location the portion of the buffer containing characters from before the Next_Location are deleted, which slides the buffer forward. Thus instead of simply extending indefinitely, the buffer is reused by moving the contents to cover the section of the character stream being processed and removing contents no longer needed. In this way character streams of indefinite length may be processed using a relatively small buffer size.

Modifications may be made to the contents of the buffer using any of the String_Buffer methods (which includes all of the StringBuffer methods). When the buffer needs to be extended, because a reference is made to a location beyond the end of the current buffer contents, input characters are always appended to the end of the existing buffer contents regardless of changes that have been made that might have altered the number of characters it contains. However, the Next_Location will not change unless specifically updated by the user, and all characters before the Next_Location will be deleted whenever the buffer contents are extended, so characters before the Next_Location should be considered consumed. Thus the String_Buffer_Reader effectively offers String_Buffer (StringBuffer) access to all characters obtained from the Reader start at the Next_Location and extending on. If the Next_Location is never moved forward (it can be moved backwards, but not before the current Buffer_Location) then all characters from the Reader are always available (this is always the case for a String source of characters), but sliding the buffer forward past consumed statements is what provides efficient use of buffer memory for indefinately long input streams.

Note: Those String_Buffer methods which search forward for character patterns will extend the buffer to the end of input if the pattern is not found. To avoid unnecessarily large buffer extensions the Read_Limit can be set to some location downstream that the application considers to be beyond where the pattern could reasonably be found.

When the character source is a String, nothing is ever read. Nevertheless, the contents of the buffer may still be appended with additional characters and the Next_Location may still be moved forward. When buffer manipulation methods attempt to extend the buffer, it will still be slid forward by deleting characters before the Next_Location. In this case virtual location values may be thought of as the number of charaters consumed (slid over) plus the number of characters in the buffer.

A special feature of this String_Buffer_Reader is its ability to handle character streams with binary sized records (as produced by DEC VMS systems). Each record in such a stream has the form:

Size Characters [ Pad ]

Where the Size is a two byte, LSB first, binary integer value specifying the number of bytes of Characters to follow. And Pad is a byte of value 0 that is present when the Size is odd. The Size bytes will be replaced with a LINE_BREAK (CR-LF) sequence, and any Pad byte will be replaced with a space character, thus providing a consistent character stream. Note: This special filtering will only be applied when the source of characters is a Reader.

@see String_Buffer @author Bradford Castalia, UA/PIRL @version 1.16 */ public class String_Buffer_Reader extends String_Buffer { public static final String ID = "PIRL.Strings.String_Buffer_Reader (1.16 2012/04/16 06:15:35)"; /*------------------------------------------------------------------------------ String buffer management: */ /** The amount to increase the size of the string buffer when it needs to be enlarged while reading a file.

It should be at least 8k to allow for the largest possible sized record on first read. */ public static final int DEFAULT_SIZE_INCREMENT = 16384; private int Size_Increment = DEFAULT_SIZE_INCREMENT; /* Input characters array. This array holds the characters input from the Character_Reader. The characters may be scanned in the array before being appended to the String_Buffer base class. This is, indeed, inefficient: it would be much better to read directly into the String_Buffer storage, but the Reader's read method will only accept a char[], and the String_Buffer (i.e. its underlying StringBuffer) does not provide direct access to its character storage in this form. Note that input data bytes are being read as char arrays which are 16-bit data types in Java. This is inefficient in terms of space, but efficient for compatibility with the standard String data type being processed. */ private char[] Input_Array = new char[Size_Increment]; // The amount of valid characters in the array (from 0). private int Input_Array_Amount = 0; /* A Reader source of characters. This will remain null if the source is a String. */ private Reader Character_Reader = null; /* String_Buffer contents locations: Location - relative to the input stream. Index - relative to the current String_Buffer contents. */ private long // Location of the first character in the buffer. Buffer_Location = 0, // Location of the next statement // (characters before this location are disposable). Next_Location = 0; /** The default {@link #Read_Limit(long) read limit}.

This class was designed for use with the PVL Parser (which is a subclass) for processing PVL syntax statements. The read limit sets the maximum amount of a file to read when working through a file label. When the limit is reached it is presumed that there are no more statements to be processed. Set it to -1 for no limit.

N.B.: Without a recognizable end-of-label marker it is quite possible for non-label file data to be interpreted as PVL statements. Thus it is advisable to set a reasonable limit on the amount of file data to read. It is typical for a PVL label to contain as one of its parameters the size of the label. This suggests a strategy of using a reasonably small limit (enough to ensure that the label size parameter, usually near the start of the PVL statements, will be read), finding this parameter, rewinding the file, and ingesting the PVL statements again using the parameter value as the limiting value. A less "intelligent" (but likely to be easier) approach is to use the default limit and simply check the validity of parameters, since applications are likely to know what parameters are valid for them. */ public static final long DEFAULT_READ_LIMIT = 16 * DEFAULT_SIZE_INCREMENT; private long // Maximum number of characters to read. Read_Limit = DEFAULT_READ_LIMIT, // Total number of characters read so far. Total_Read = 0; /** The Read_Limit size value that means what it says.

@see #Read_Limit(long) */ public static final long NO_READ_LIMIT = -1; /* The number of non-text chars that will end data input. To allow for possible binary record size bytes and padding this value must be at least 4. */ private int Non_Text_Limit = 4, Non_Text_Count = 0; private static String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_DATA = 1 << 0, DEBUG_BUFFER = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors: */ /*------------------------------------------------------------------------------ File input */ /** Creates a String_Buffer_Reader with the reader as the source of characters up to the specified limit.

@param reader The Reader to use as the source of input. @param limit The maximum number of character bytes to read. @see #Read_Limit(long) */ public String_Buffer_Reader ( Reader reader, long limit ) {Set_Reader (reader).Read_Limit (limit);} /** Creates a String_Buffer_Reader with the reader as the unlimited source of characters.

@param reader The Reader to use as the source of input. */ public String_Buffer_Reader (Reader reader) {this (reader, 0);} /** Creates a String_Buffer_Reader with no character source.

@see #Set_Reader(Reader) */ public String_Buffer_Reader () {} /*------------------------------------------------------------------------------ String input */ /** Creates a String_Buffer_Reader with a String source of characters.

@param string The String to use as the source of input. */ public String_Buffer_Reader ( String string ) { append (string); Total_Read = string.length (); } /*============================================================================== Accessors. */ /** Sets the Reader to use as the source of characters.

Changing the Reader in mid stream may have unexpected side effects. Any data still being held in the internal character array pending further processing before transfer to the charactr buffer will remain in the virtual input stream before data read from the new Reader (note that for a {@link #String_Source() String source of characters} the internal character array is not used). The Total_Read will not be reset by a change of Reader; i.e. a single virtual character input stream is seen.

Note: If the reader is set to null, then input has, by definition, ended, but the current contents of the buffer remain available. However, any data still pending processing in the internal character array is dropped. If this object was created with a String character source a Reader may be set to supplement the initial String. In this case the Total_Read will include the length of the initial String.

@param reader The reader to associate with this object. @return This Buffer_String_Reader. @see #Total_Read() @see #String_Source() @see #Extend() */ public String_Buffer_Reader Set_Reader ( Reader reader ) { Character_Reader = reader; if (String_Source ()) Input_Array_Amount = 0; return this; } /** Gets the Reader used as the source of characters.

@return The Reader associated with this object; will be null if the object currently has no Reader (e.g. it was created with a String character source). @see #Set_Reader(Reader) */ public Reader Get_Reader () {return Character_Reader;} /** Tests if the source of characters is a Reader.

@return true if the source of characters is a Reader. */ public boolean Reader_Source () {return (Character_Reader != null);} /** Tests if the source of characters is a String (there is no Reader).

@return true if the source of characters is a String. */ public boolean String_Source () {return (Character_Reader == null);} /** Reset as if (almost) nothing happened.

The {@link #Buffer_Location() buffer location} and {@link #Next_Location() next location} in the stream are reset to zero, the internal character buffer is marked as empty. If a {@link #Reader_Source() Reader source} (rather than {@link #String_Source() String source}) is being used the {@link #Total_Read() total read} is set to zero, the {@link #Read_Limit() read limit} set to {@link #NO_READ_LIMIT} and the base String_Buffer is cleared. N.B.: The internal character buffer {@link #Size_Increment() size increment} and {@link #Non_Text_Limit() non-text data threshold} are not changed.

If {@link #Filter_Input() input filtering} is enabled it is reset so the source stream will be tested again. */ public String_Buffer_Reader Reset () { Buffer_Location = 0; Next_Location = 0; Input_Array_Amount = 0; if (Reader_Source ()) { Total_Read = 0; Read_Limit = NO_READ_LIMIT; clear (); } if (Sized_Record != NO_FILTERING) { Sized_Record = CHECK_FOR_SIZED_RECORDS; Padding = 0; LSB_Character = 0; } return this; } /** Gets the location in the input stream of the index in the buffer.

Location values must be used when manipulating the contents of the buffer using String_Buffer_Reader methods.

Location values are virtual with respect to the entire character input stream. Location 0 corresponds to the first character consumed after the buffer has slid forward, or the first character currently in the buffer if nothing has yet been consumed. If the number of characters in the buffer is only changed when it is {@link #Extend() Extend}ed, then location values are relative to all characters in the input stream. However, since the number of characters in the buffer may be changed by various methods, location values are actually relative to all characters that the buffer has slid over (been consumed) plus the current contents of the buffer; i.e. the virtual input stream.

Note: Location values are long; index values are int.

@param index A buffer index value. @return The corresponding input stream location. @see #Extend() */ public long Location (int index) {return Buffer_Location + index;} /** Gets the index in the buffer for the location in the input stream.

Index values are relative to the current contents of the buffer; index 0 corresponds to the character currently at the beginning of the buffer. Index values must be used when menipulating the contents of the buffer with String_Buffer (StingBuffer) methods.

Note: Location values are long; index values are int.

@param location A location in the input stream. @return The corresponding buffer index relative to the current location of the beginning of the buffer in the input stream. */ public int Index (long location) {return (int)(location - Buffer_Location);} /** Gets the current location of the beginning of the buffer in the virtual stream.

@return The location of the first character in the buffer. */ public long Buffer_Location () {return Buffer_Location;} /** Resets the virtual stream location values.

The current {@link #Buffer_Location() character buffer location} in the source of charcters is subtracted from the {@link #Next_Location() next location} and the buffer location is set to zero. The {@link #Total_Read() total read} is also set to zero if the source of characters is a {@link #Reader_Source () stream reader}, not a String.

A reset has the effect of making it appear as if the character buffer had been loaded for the first time, but without affecting the current relative location of the next character to be read.

Because the location values are long integers they can be exepected to remain valid even for a reader that is a coninuously generated stream (such as a network socket). Nevertheless, a reset when the next location exceeds some (very large) threshold will ensure that a stream of unlimited length can be continuously processed. */ public String_Buffer_Reader Reset_Location () { Flush (Next_Index ()); Next_Location -= Buffer_Location; Buffer_Location = 0; if (Reader_Source ()) Total_Read = 0; return this; } /** Sets the next location to position the buffer when sliding it forward.

If the location is beyond the end of the current buffer contents the buffer is extended to include the location, if possible.

@param location The next location to use for the beginning of the buffer when the buffer is slid forward. @return The new value of the next location. The only case where this will be different from the specified location is when the end of input has been reached before the specified location could be reached; in which case the return value is the final end location. @throws IndexOutOfBoundsException If the location is before the beginning of the buffer. @throws IOException If there was a problem extending the buffer. @see #End_Location() @see #Extend() */ public long Next_Location ( long location ) throws IndexOutOfBoundsException, IOException {return (Next_Location = Location (get_index (location)));} /** Gets the current value of the next location.

@return The current value of the next location. @see #Next_Location(long) */ public long Next_Location () {return Next_Location;} /** Sets the Next_Location using a buffer index value.

@param index The buffer index value for the next location. @return The index value of the new next location. @throws IndexOutOfBoundsException If the index is before the beginning of the buffer. @throws IOException If there was a problem extending the buffer. @see #Next_Location(long) */ public int Next_Index (int index) throws IndexOutOfBoundsException, IOException { int next_index = get_index (Location (index)); Next_Location = Location (next_index); return next_index; } /** Gets the index in the buffer of the Next_Location.

@return The index in the buffer of the next location. @see #Next_Location() */ public int Next_Index () {return (int)(Next_Location - Buffer_Location);} /** Gets the location immediately following the last character in the buffer.

@return The location immediately following the last character in the buffer. */ public long End_Location () {return Buffer_Location + length ();} /** Gets the buffer index of the last character in the buffer. This is the same as the number of characters currently in the buffer.

@return The buffer index of the last character in the buffer. */ public int End_Index () {return length ();} /** Sets the location where character input is to stop; the maximum number of characters to be obtained from the source.

If the limit is 0, then the DEFAULT_READ_LIMIT (256 KB) is used. If the limit is less than 0, then NO_READ_LIMIT will be used, so no upper limit will be imposed on the number of characters to obtain from the source.

Note: The read limit is reset to the total number of characters read if a Reader encounters the end of the input stream.

Note: The read limit will have no effect {@link #String_Source() when the character source is a String}. In this case nothing is ever read so the read limit is never changed.

Note: When the {@link #Non_Text_Limit(int) threshold for sequential non-text data} has been reached while extending the buffer, then the read limit is automatically set to the number of characters read before the non-text sequence. Once this condition has been encountered, the read limit can not be changed without first resetting the non-text data threshold up to a higher value.

@param limit The location where character input is to stop. @return This String_Buffer_Reader. @see #NO_READ_LIMIT */ public String_Buffer_Reader Read_Limit ( long limit ) { if (Reader_Source () && Non_Text_Count < Non_Text_Limit) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (">>> String_Buffer_Reader.Read_limit:" + limit); if (limit > 0) Read_Limit = limit; else if (limit == 0) Read_Limit = DEFAULT_READ_LIMIT; else Read_Limit = NO_READ_LIMIT; if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< String_Buffer_Reader.Read_limit: " + Read_Limit); } return this; } /** Sets the read limit to NO_READ_LIMIT.

@return This String_Buffer_Reader. @see #Read_Limit(long) */ public String_Buffer_Reader No_Read_Limit () {return Read_Limit (NO_READ_LIMIT);} /** Gets the current limit where reading characters is to stop; the maximum number of characters to input from the Reader.

This will be NO_READ_LIMIT if there is no limit to the number of characters to read from the source.

@return The current read limit. */ public long Read_Limit () {return Read_Limit;} /** Gets the total number of characters read so far. This value is the actual number of characters input from the Reader regardless of any subsequent use of the data.

@return The total number of characters read. */ public long Total_Read () {return Total_Read;} /** Tests if the end of input has been reached.

The end of input occurs when the read limit has been reached. When the input source is a String, then it has always ended (i.e. there is nothing to read).

@return true if the character source has ended. */ public boolean Ended () { return (Read_Limit != NO_READ_LIMIT && Total_Read >= Read_Limit) || String_Source (); } /** Tests if there are no more characters at the Next_Location; the source of characters is effectively empty.

There will be no more characters at the Next_Location if the input has eneded and the Next_Location is at, or beyond, the End_Location.

@return true if there are no more statements available. @see #Ended() @see #End_Location() */ public boolean Is_Empty () {return (Ended () && Next_Location >= End_Location ());} /** Tests if the specified location is at or beyond the End_Location.

Note: Unlike Ended, Is_End only indicates that the location is beyond the current buffer contents. There may still be more source characters available.

@param location The location to test. @return true if the location is at or beyond the End_Location. @see #End_Location() */ public boolean Is_End (long location) {return location >= End_Location ();} /** Sets the size increment by which the input buffer will be extended when it is slid forward in the Reader's input stream.

If the amount is less than or equal to 0, then the default size increment (16 KB) will be applied. If the amount is less than or equal to the Non_Text_Limit threshold, then it will be increased by that amount. If the current internal character array contains more input data that the size increment, then it will be set to the amount of data being held. If the new size increment is different from the length of the current internal character array, then a new array will be allocated and any data in the old array will be copied into it.

@param amount The new size increment. @return This String_Buffer_Reader. @see #Non_Text_Limit(int) @see #Extend() */ public String_Buffer_Reader Size_Increment ( int amount ) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (">>> String_Buffer_Reader.Size_Increment: " + amount); if (amount <= 0) Size_Increment = DEFAULT_SIZE_INCREMENT; else Size_Increment = amount; if (Size_Increment <= Non_Text_Limit) Size_Increment += Non_Text_Limit; if (Size_Increment < Input_Array_Amount) Size_Increment = Input_Array_Amount; if (Input_Array.length != Size_Increment) { // Re-allocate the array for input characters. if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Reallocating Input_Array: " + Size_Increment); char[] new_array = new char[Size_Increment]; // Copy the input data to the new array. for (int index = 0; index < Input_Array_Amount; index++) new_array[index] = Input_Array[index]; Input_Array = new_array; } if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< String_Buffer_Reader.Size_Incrment: " + Size_Increment); return this; } /** Gets the size increment by which the input buffer will be extended when it is slid forward in the Reader's input stream.

@return The current size increment. @see #Size_Increment(int) */ public int Size_Increment () {return Size_Increment;} /** Sets the length of a non-text data sequence that will cause input from the Reader's character stream to the the buffer to stop.

Text characters are recognized by the Is_Text method; characters that fail this test are non-text.

Since this class is managing a String buffer, it is assumed that non-text data from the input should not be transferred into the buffer; i.e. the data are not valid String characters. However, to allow for possible input filter requirements (e.g. binary record size values), a sequence of non-text data less than the limit is allowed into the character buffer. When a sequence of non-text data in the input stream reaches the limit the Read_Limit is set to the amount read up to, but not including, the sequence of non-text data. A limit of 0 has the same effect as a limit of 1 (so a limit of zero will be forced to 1); i.e. no non-text data are acceptable. Specifying a negative limit disables non-text data checking; i.e. any non-text data is acceptable.

Note: No sequence of non-text data as long as the limit amount is allowed into the object's character buffer; shorter sequences are allowed to pass. And since the Read_Limit is set to the location in the input stream immediately preceeding a limiting non-text sequence, the buffer will not be {@link #Extend() Extend}ed beyond this location. However, the Non_Text_Limit may subsequently be increased and the Read_limit lifted (in that order; the Read_limit can not be changed while a Non_Text_Limit block is in effect) to allow further processing of the input stream (data read from the Reader but not transferred to the character buffer is never lost). Nevertheless, any non-text data sequence outstanding will remain in effect and will be included in counting the length of the next non-text sequence; the non-text sequence length is only reset to 0 when a text character is seen in the input stream.

Warning: The Non_Text_Limit may affect the operation of an input filter. The input filter provided with this class requires, if {@link #Filter_Input(boolean) input filtering is enabled}, a Non_Text_Limit of a least 4.

@param limit The maximum allowed sequence of non-text input data. @return This String_Buffer_Reader. @see #Is_Text(char) @see #Read_Limit(long) @see #Extend() */ public String_Buffer_Reader Non_Text_Limit ( int limit ) { if (limit == 0) // Accept no non-text data (stop on first occurance). limit = 1; else if (limit < 0) // Accept all non-text data bytes. limit = -1; Non_Text_Limit = limit; return this; } /** Get the current limit for non-text data input.

@return The non-text data limit. @see #Non_Text_Limit(int) */ public int Non_Text_Limit () {return Non_Text_Limit;} /*============================================================================== Buffer Management. */ /** Extend the string buffer with additional input characters.

The procedure to extend the buffer has several steps:

Test if input filtering is enabled.
The {@link #Filter_Input() Filter_Input} test is done before reading any data so the Reader input position is not altered. The test method may need to read characters from the source at its current position.
Slide the buffer forward.
If the {@link #Next_Location(long) Next_Location} is beyond the {@link #Buffer_Location() Buffer_Location} then the consumed contents of the buffer - i.e. from the beginning up to, but not including, the Next_Index - are deleted.
Check for end of input.
The end of input occurs when the end of the Reader's data stream has been reached, or the amount of input has reached the {@link #Read_Limit(long) Read_Limit}. When this object was constructed from a String the end of input has been reached by definition. When the end of input has been reached the buffer can not be extended so the method returns false.
Determine how much to extend the buffer.
The buffer will be extended by the lesser of the amount of free space in the internal character array or the amount from the current {@link #Total_Read() Total_Read} up to the Read_Limit. Of course, if there is no read limit then the former is always used. The internal character array, where characters read from the Reader are stored for checking before being transferred to the object's buffer, has a length of {@link #Size_Increment(int) Size_Increment}. However, during each read cycle less than the entire character array contents may be transferred to the buffer; the remainder is carried over in the array where new input data is added in the next read cycle. Thus the buffer will be extended by no more than Size_Increment characters.
Read characters.
Characters are read from the Reader into an internal character array. Here they may be scanned for non-text data before being appended to the object's character buffer. This cycle continues until the amount to extend the buffer has been read or the end of the input stream is encounterd.
Check for non-text data.
Characters read into the internal storage array may be scanned for a sequence of non-text data of the current maximum length. This check is only done if the {@link #Non_Text_Limit(int) non-text data threshold} is non-negative. Counting of sequential non-text data is continuous across buffer extensions and is only reset to zero when a text character is found. If the non-text data threshold is reached, then only the data preceeding the sequence is appended to the object's character buffer and the Read_Limit is reset to the corresponding position in the data stream. Shorter sequences of non-text data at the end of scanned input are also omitted from the transfer to the buffer, against the possibility of more non-text data from the next read, but the Read_Limit is not changed. Data not transferred to the buffer is retained in the internal array where it is added to in the next read cycle.
Transfer characters to the buffer.
The data in the character array, from the beginning up to but not including any trailing non-text data (if non-text data checking is enabled), is appended to the object's character buffer.
Filter the new characters.
If input filtering is enabled, the {@link #Filter_Input(int) Filter_Input} method is invoked on the new characters.

Note: Altering the Reader's input position in the character stream may have have unpredictable consequences. The impact on the operation of the Filter_Input method in particular could be fatal. Without input filtering, however, it is quite possible to move the Reader's input position (e.g. by reset, skip, or read operations) to suit application needs. It is important to keep in mind that any such alterations will go undetected, and the value of the Total_Read and source stream locations will continue to indicate the amount of data actually read and consumed from a virtually sequential input stream.

@return true if more input is available; false if the end of input was reached. @throws IOException From the read method of the Reader. @throws IndexOutOfBoundsException If the buffer indexes are invalid. @see #Filter_Input() @see #Next_Location(long) @see #Buffer_Location() @see #Next_Index() @see #Ended() @see #Read_Limit(long) @see #Total_Read() @see #Size_Increment(int) @see #Is_Text(char) @see #Non_Text_Limit(int) @see #Filter_Input(int) */ public boolean Extend () throws IndexOutOfBoundsException, IOException { if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (">>> String_Buffer_Reader.Extend"); /* >>> WARNING <<< Do the Filter_Input test BEFORE reading any data so the Reader input position is not altered for the first test (which, for the default test method, will read the next two input characters the first time it is used). */ boolean filter_input = Filter_Input (); int next_index = Math.min (Next_Index (), End_Index ()); if (next_index > 0) { // Free the consumed data. try { delete (0, next_index); } catch (StringIndexOutOfBoundsException exception) { throw new IndexOutOfBoundsException (Exception_Message ( "Invalid Next_Index during buffer Extend!" + NL + " Buffer_Location = " + Buffer_Location + NL + " Next_Location = " + Next_Location + " (index " + next_index + ')' + NL + " End_Location = " + End_Location () + " (index " + End_Index () + ')' + NL + ((exception.getMessage () == null) ? "" : NL + exception.getMessage ()), Total_Read )); } // Update the location of the first character in the String_Buffer. Buffer_Location += next_index; } if (Ended () || Non_Text_Count >= Non_Text_Limit) { /* The end of input has been reached. When the non-text data input threshold is first encountered after reading input (below) the read limit is set to the input location before the non-text data. So this condition suggests that the read limit must have been changed (user set the max up, the limit up, then the max down?). In any case, this is an end of input condition. */ if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Ended" + NL +"<<< String_Buffer_Reader.Extend"); return false; } /* The curent end index before refilling the buffer where the new characters will start. */ int start_index = End_Index (); // Read more data. int read_amount, amount_read; /* Determine how far to extend the buffer: the lesser of the free space in the Input_Array or the amount from the Total_Read up to the Read_Limit. The Input_Array.length should be the Size_Increment. */ read_amount = Input_Array.length - Input_Array_Amount; if (Read_Limit != NO_READ_LIMIT) read_amount = (int)Math.min ((long)read_amount, Read_Limit - Total_Read); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Extend: read amount = " + read_amount); Read_Data: while (read_amount > 0) { try { // Append to the end of any remaining data. if ((amount_read = Character_Reader.read (Input_Array, Input_Array_Amount, read_amount)) < 0) { // End of input encounterd. Read_Limit = Total_Read; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Ended" + NL +"<<< String_Buffer_Reader.Extend"); break; } } catch (IOException exception) { throw new IOException (Exception_Message ( "While reading " + read_amount + " characters." + ((exception.getMessage () == null) ? "" : NL + exception.getMessage ()), Total_Read )); } // Update the totals. Total_Read += amount_read; Input_Array_Amount += amount_read; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Extend:" +" read amount = " + read_amount + NL +" amount read = " + amount_read + NL +" buffered = " + Input_Array_Amount + NL +" total read = " + Total_Read); read_amount -= amount_read; // The amount left to read. if (Non_Text_Limit >= 0) { /* Input stops when a sequence of Non_Text_Limit data bytes are detected. Since this class is managing a String buffer, it is assumed that non-text data from the input should not be transferred into the buffer; i.e. the data is not valid String characters. However, to allow for possible input filter requirements (e.g. binary record size values), a sequence of non-text data less than Non_Text_Limit is allowed into the character buffer. Note that the sequence of non-text data may start before the end of the array, so the count of non-text data is maintained across buffer extends. The accumulator of non-text data bytes encountered will not be reset unless a text data byte is seen. Note that a Non_Text_Limit value of 0 means that any non-text data will qualifies as the end of input. Trailing non-text data is not transferred from the internal character array into the object buffer. This ensures that a max non-text sequence will never be placed in the buffer. */ next_index = Non_Text_Count; // Skip previously scanned data. while (next_index < Input_Array_Amount) { if (Is_Text (Input_Array[next_index++])) Non_Text_Count = 0; else if (++Non_Text_Count >= Non_Text_Limit) { // Max non-text data; stop before this sequence. Read_Limit = Total_Read - Input_Array_Amount + next_index - Non_Text_Count; read_amount = 0; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Extend: max non-text (" + Non_Text_Limit + ") at index " + (next_index - 1) + NL +" Read limit = " + Read_Limit); break; } } // Leave trailing non-text data for the next buffer extend. next_index -= Non_Text_Count; } else next_index = Input_Array_Amount; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Extend: append (Input_Array, 0, " + next_index + ");"); append (Input_Array, 0, next_index); Input_Array_Amount -= next_index; // Move the remaining data to the front of the buffer. Flush (next_index); } if (filter_input && start_index < End_Index ()) // Apply the input filter to the new data. Filter_Input (start_index); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Buffer_Location = " + Buffer_Location + NL +" Next_Location = " + Next_Location + NL +" End_Location = " + End_Location () + NL +" Read_Limit = " + Read_Limit + NL +" total read = " + (End_Index () - start_index) + NL +" Total_Read = " + Total_Read + NL +" Ended = " + Ended () + NL +"<<< String_Buffer_Reader.Extend"); return (! Ended ()); } /** Flush data from the front of the Input_Array.

The Input_Array_Amount of data from the start index is copied to the front of the Input_Array.

@param start The index of the first valid datum in the Input_Array. */ private void Flush ( int start ) { if (Input_Array_Amount <= 0 || start <= 0 || start >= Input_Array.length) return; int end = start + Input_Array_Amount; if (end > Input_Array.length) end = Input_Array.length; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (">-< String_Buffer_Reader.Flush: Moving " + (end - start) + " characters from " + start + " to the buffer front."); int index = 0; while (start < end) Input_Array[index++] = Input_Array[start++]; } /** Tests if a character is considered to be text.

Text is evaluated against the ASCII code set and includes all printable characters - values from 0x20 (' ') to 0x7E ('~') inclusive - plus the usual format control characters - HT, LF, VT, FF, and CR (0x9 to 0xD inclusive).

@param character The character to be tested. @return true if the character is text; false otherwise. */ protected boolean Is_Text ( char character ) { return ((character >= 0x20 && character < 0x7F) || // Printable text (character >= 0x9 && character <= 0xD)); // HT,LF,VT,FF,CR } // Special variables for handling sized record format files: /** The "sized record" problem:

Some files use a record structure which is composed of a leading size value (16-bits, LSB first) followed by size bytes of data. This is like Pascal strings (and just as cumbersome). Unfortunately, sized records add another layer of processing logic for the tolerant design goal.

The solution, here, is to first test for the size value in the first two bytes read and, if present, flag this condition so that string buffer refills will be cleaned up. The test for the presence of the size value depends on two assumptions: 1) the file is currently positioned at the beginning of a size value, and 2) the MSB of the size value maps to an unprintable character (i.e. < 32). The first assumption depends on the user. The second assumption depends on the first record having less than 8k bytes.

The string buffer will continue to be processed as usual, but the string buffer will be cleaned up after each read by replacing the record size values with LINE_BREAK characters. To keep track of where the record size values are located in the string buffer, the offset from "last" to the next size value is recorded in "Sized_Record". Since this is also used as a flag for the sized records format of the data, this value must not be zero. Therefore, if this value would be 0, it is set to -1 instead. It's possible for a size value to be split across a buffer read. In this case the recorded value is set to -2, the first (LSB) byte of the size value is saved in the String_Buffer "LSB_Character" entry, and the first byte of the next read is known to be the value of the next (MSB) byte (see the padded record exception below). Thus the interpretation of Sized_Record is as follows:

&npsp;0 - Not a sized record file.
>0 - Offset from last to next size value (see padded exception).
-1 - Offset is 0.
-2 - Next char after last is second byte (MSB) of size value.
The value of the first byte is stored in "LSB_Character".
-3 - Check for sized records hasn't been done yet.

While one might think that because all sized records are guaranteed to be of even size - by padding, when needed, which adds it's own problems - that an even sized buffer would prevent the possibility of a split size value, the unpredictable nature of the statements and the manner of processing in a sliding window buffer could result in the buffer becoming non-aligned with respect to file records. Therefore, the possibility of split size values must be taken into account.

The record size value is the number of characters in the next record (which many, of course, not be a complete statement). This value does not include the two chars of the size value itself, or a possible zero valued char that is appended to the record whenever its size is odd. This pad byte presents the problem of prematurely terminating the string presumed to contain all of the statements. Therefore it is plugged with a space character whenever it occurs. To keep track of odd-sized, and thus padded records, across buffer refills, a "padded" flag entry is set accordingly at the end of cleaning up each new buffer refill. This value (0 if no pad byte, 1 otherwise) allows the Sized_Record value to be adjusted accordingly. */ private int Sized_Record = CHECK_FOR_SIZED_RECORDS, Padding = 0; private char LSB_Character = 0; private static final int NO_FILTERING = 0, OFFSET_ZERO = -1, NEXT_AFTER_LAST_IS_MSB = -2, CHECK_FOR_SIZED_RECORDS = -3; private static final char[] LINE_BREAK = {'\r', '\n'}; /** Tests if the character source will be filtered on input.

Some files - notably binary data files produced by DEC VMS systems - use a record structure composed of a leading binary size value (16-bits, LSB first) followed by size bytes of data. This is like Pascal strings (and just as cumbersome). In addition, if the size is odd then a zero-valued pad byte will be appended to the record (to force word alignment for all size values). Binary record size values are detected by testing the second (MSB) character obtained from the Reader (String character sources are not tested) for a value less than 32 (' ') but not 9 (HT), 10 (NL) or 13 (CR). This test for the presence of the binary size value depends on a few assumptions: 1) the Reader is positioned at the beginning of a potential binary size value (this is probably the beginng of the file or stream), 2) the second character of normal (unfiltered) input will not be unprintable, and 3) the first binary size value is less than 8k and outside the ranges (2304-2815) and (3328-3583). N.B.: This last assumption is rather risky, of course; but these records are very likely to be less than 2k in size. Of course, this also requires that any character encoding preserve the values of bytes in the stream.

During character input a source that has sized records will be filtered: The record size bytes will be replaced with a LINE_BREAK (CR-LF) sequence, and any pad bytes will be replaced with a space character, thus providing a consistent character stream.

When a String_Buffer_Reader is created its input filtering status is untested. If the input filtering status is untested when this method is used then the next two characters are read (if the source is a Reader) unconditionally - i.e. regardless if the source has already been read or characters otherwise put in the buffer - and they are tested. The results of the input source test update the input filtering status to either filtered or unfiltered, so subsequent use of this method will not repeat the input source test unless the input filtering status is reset to the initial untested state (which may be done by the {@link #Filter_Input(boolean) Filter_Input (boolean)} method.

Note: To accommodate binary record size values the Non_Text_Limit threshold must be greater than 3 (for a possible pad byte followed by two record size bytes).

@return true if input filtering will be applied, false otherwise. @throws IOException If the Reader could not be read during the initial check. @see #Filter_Input(boolean) @see #Filter_Input(int) @see #Non_Text_Limit(int) */ public boolean Filter_Input () throws IOException { if (Sized_Record == CHECK_FOR_SIZED_RECORDS) { if (String_Source ()) return false; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (">>> String_Buffer_Reader.Filter_Input"); int count = 2, amount = 0; while (amount < 2) { try { count = 2 - amount; if ((count = Character_Reader.read (Input_Array, Input_Array_Amount + amount, count)) < 0) { // End of input encounterd. Read_Limit = Total_Read + amount; break; } } catch (IOException exception) { throw new IOException (Exception_Message ( "While reading " + count + " characters." + ((exception.getMessage () == null) ? "" : NL + exception.getMessage ()), Total_Read + amount )); } amount += count; } Total_Read += amount; char character = Input_Array[Input_Array_Amount + 1]; if (amount >= 2 && Input_Array[Input_Array_Amount + 1] < 0x20 && // ' ' Input_Array[Input_Array_Amount + 1] != 0x09 && // Input_Array[Input_Array_Amount + 1] != 0x0a && // Input_Array[Input_Array_Amount + 1] != 0x0d // ) { // Sized records detected. if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Initial check - true"); // Set the offset to the location of this binary record size data. if ((Sized_Record = Input_Array_Amount) == 0) Sized_Record = OFFSET_ZERO; } else { // Sized records were not detected. if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" Initial check - false"); Sized_Record = NO_FILTERING; } // Update the amount of data in the array. Input_Array_Amount += amount; if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println ("<<< String_Buffer_Reader.Filter_Input: " + (Sized_Record != NO_FILTERING)); } return Sized_Record != NO_FILTERING; } /** Enable or disable input filtering.

If input filtering is to be allowed and it is currently not enabled, the input filtering status is reset to untested. If input filtering is not to be allowed it is unconditionally disabled.

@param allow true if input filtering is allowed; false otherwise. @return This String_Buffer_Reader. @see #Filter_Input() @see #Filter_Input(int) */ public String_Buffer_Reader Filter_Input ( boolean allow ) { if (allow) { if (Sized_Record == NO_FILTERING) Sized_Record = CHECK_FOR_SIZED_RECORDS; // Input test required. } else Sized_Record = NO_FILTERING; return this; } /** Filters the current contents of the character buffer, starting at the specified index and continuing to the end of the buffer contents.

The details of the filtering implemented here is described by the {@link #Filter_Input() Filter_Input} test method.

@param index The index in the character buffer where filtering is to start. Nothing is done if the index is invalid (i.e. < 0 or >= the end of characters in the buffer). @throws IndexOutOfBoundsException An index for record size bytes or padding is invalid. Assuming that the filtering algorithm is bug free (8^]), then the input stream has been corrupted, possible due to inappropriate changes to its position by the application. It is very likely in this case that previous character buffer contents have been mangled as well. @throws IOException This implmentation doesn't do anything that could generate an IOException. However, the method is marked this way so sbuclass implementations that need to use the Reader may throw this exception. @see #Filter_Input() @see #End_Index() @see #Record_Size(char, char) */ protected void Filter_Input ( int index // Previous end index. ) throws IndexOutOfBoundsException, IOException { if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (">>> String_Buffer_Reader.Filter_Input: from index " + index); if (index < 0 || index >= End_Index ()) return; int record_size = 0; try { // Pick up where we left off. if (Sized_Record > 0) record_size = Sized_Record; else if (Sized_Record == OFFSET_ZERO) record_size = 0; else if (Sized_Record == NEXT_AFTER_LAST_IS_MSB) { /* Split record size value. Reassemble it and fill the MSB hole. */ record_size = Record_Size (LSB_Character, charAt (index)); setCharAt (index, LINE_BREAK[1]); } // Plug the holes in the sized records. for (index += record_size; // Next (pad and) record size bytes. (index + Padding + 1) < End_Index (); index += record_size) { if (Padding > 0) { // Patch the null pad char with a space. if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Filter_Input: pad at " + index); setCharAt (index++, ' '); } record_size = Record_Size (index); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Filter_Input: record size " + record_size + " at " + index +" (next size at " + (index + 2 + record_size + (record_size % 2)) + ")"); Padding = record_size % 2; // Is there a pad? // Replace the 2 record size chars with LINE_BREAK chars. setCharAt (index++, LINE_BREAK[0]); setCharAt (index++, LINE_BREAK[1]); } // Check for possible trailing pad byte. if (index < End_Index () && Padding > 0) { if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_Reader.Filter_Input: trailing pad at " + index); setCharAt (index++, ' '); } // Check for split size value. if ((index + 1) == End_Index ()) { Sized_Record = NEXT_AFTER_LAST_IS_MSB; LSB_Character = charAt (index); if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println (" String_Buffer_ReaderFilter_Input: split size at " + index); setCharAt (index, LINE_BREAK[0]); } /* Set the offset from the end index to where this process will pick up again after the next buffer refill. */ else if ((Sized_Record = index - End_Index ()) == 0) Sized_Record = OFFSET_ZERO; } catch (IndexOutOfBoundsException exception) { throw new IndexOutOfBoundsException (Exception_Message ( "Lost record sizing bytes for Filter_Input!" + NL + " Location = " + Location (index) + " (index " + index + ')' + NL + " Record size = " + record_size + NL + " Padding = " + Padding + NL + " Buffer location = " + Buffer_Location + NL + " Buffer length = " + length () + NL + " Buffer capacity = " + capacity () + NL + ((exception.getMessage () == null) ? "" : NL + exception.getMessage ()), Total_Read )); } if ((DEBUG & DEBUG_BUFFER) != 0) System.out.println ("<<< Filter_Input: Sized_Record = " + Sized_Record); } /** Converts a 16-bit (in 2 sequential chars), LSB-first value at the buffer index to a record size value.

This method is used by the input filter.

@param index The index of the binary record size bytes in the buffer. @return A record size value. @see #Record_Size(char, char) */ protected int Record_Size ( int index ) { return Record_Size ( charAt(index), charAt(index + 1) ); } /** Converts two char values to a single int value, assuming that the first char value is the LSB and the second the MSB of a 16-bit integer.

Bits 0-7 of the MSB are shifted left 8 bits and ORed with bits 0-7 of the LSB to form the integer value.

@param LSB The Least Significant Byte of the 16-bit record size value. @param MSB The Most Significant Byte of the 16-bit record size value. @return A record size value. @see #Filter_Input() */ protected static int Record_Size ( char LSB, char MSB ) { return (int)(((MSB & 0xFF) << 8) | (LSB & 0xFF)); } /*============================================================================== Utility functions: */ /* String_Buffer-like methods using virtual stream locations. The location arguments are not limited to the current contents of the character buffer; the buffer will be extended as needed to bring the specified location into the buffer, until the end of input data is reached. >>> NOTE <<< Methods that search will ingest all available data if the search is not satisfied. There is thus a big memory consumption penalty for failed searches. */ /** A character that should not occur in any valid String.

@see CharacterIterator#DONE */ public static final char INVALID_CHARACTER = CharacterIterator.DONE; /** Gets the character at the specified location in the virual stream.

The current contents of the character buffer will be automatically extended to the specified location.

@param location The location from which to get a character. @return The char found at the location. If the location is invalid for any reason then the INVALID_CHARACTER is returned. */ public char Char_At ( long location ) { try {return charAt (get_index (location));} catch (IndexOutOfBoundsException exception) {return INVALID_CHARACTER;} catch (IOException exception) {return INVALID_CHARACTER;} } /** Gets the substring including the characters from the start location up to, but not including, the end location in the virtual stream.

The current contents of the character buffer will be automatically extended to include the specified locations. If the substring extends beyond the end of input location, then that portion up to the end of input will be returned. If both start and end locations are beyond the end of input, then an empty String will be returned.

@param start The location of the first character of the substring. @param end The location of end of the substring (the location immediately following the last character in the substring). @return The String from the start location, inclusive, to the end location, exclusive. @throws IndexOutOfBoundsException A location is before the Buffer_Location, or start is greater than end. @throws IOException While reading characters to extend the buffer. */ public String Substring ( long start, long end ) throws IndexOutOfBoundsException, IOException { int first, last; /* Make sure the end of the string is in the buffer first, in case this causes the start location move in the buffer as a result of sliding forward. */ last = get_index (end); first = get_index (start); return substring (first, last); } /** Skips over a character set in the virtual stream.

Starting at the specified location find the location of the next character that is not in the skip string. The skip string is not a pattern; i.e. a character in the virtual stream that matches any character in the skip string is skipped during the search. The return location will be for a character that does not occur in the skip string.

The character buffer will be extended until a non-skip character is found. Note: The character buffer will be extended to include all available input if all characters from the beginning location on are in the skip String.

@param location The location from which to start the search. @param skip The String containing characters to be skipped. @return The location of the next character not in the skip String. This will be the End_Location if the end of input data is reached (i.e. all characters from the start location on are in the skip String). @throws IndexOutOfBoundsException The location is before the Buffer_Location. @throws IOException While reading characters to extend the buffer. */ public long Skip_Over ( long location, String skip ) throws IndexOutOfBoundsException, IOException { while (Is_End (location = Location (skip_over (get_index (location), skip))) && ! Ended ()); return location; } /** Skips until a member of the character set is found in the virtual stream.

Starting at the specified location find the location of the next character that is in the find string. The find string is not a pattern; i.e. a character in the virtual stream that matches any character in the find string satisfies the search. The return location will be for a character that occurs in the find string.

The character buffer will be extended until a find character is found. Note: The character buffer will be extended to include all available input if all characters from the beginning location on are not in the find String.

@param location The location from which to start the search. @param find The String containing characters to be found. @return The location of the next character also in the find String. If the end of input data is reached (i.e. all characters from the start location on are not in the find String), then -1 is returned. @throws IndexOutOfBoundsException The location is before the Buffer_Location. @throws IOException While reading characters to extend the buffer. */ public long Skip_Until ( long location, String find ) throws IndexOutOfBoundsException, IOException { while ((location = Location (skip_until (get_index (location), find))) < Buffer_Location && ! Ended ()) location = End_Location (); return (location < Buffer_Location) ? -1 : location; } /** Finds the next location of the pattern String in the virtual stream.

Starting at the specified location find the location of the next occurance of the substring that matches the pattern String.

The character buffer will be extended until the pattern substring is found. Note: The character buffer will be extended to include all available input if the pattern can not be found.

@param location The location from which to start the search. @param pattern The String to be found as a substring of the virtual stream. @return The location of the beginning of the pattern substring. If the end of input data is reached without finding the pattern String, then -1 is returned. @throws IndexOutOfBoundsException The location is before the Buffer_Location. @throws IOException While reading characters to extend the buffer. */ public long Location_Of ( long location, String pattern ) throws IndexOutOfBoundsException, IOException { while ((location = Location (index_of (get_index (location), pattern))) < Buffer_Location && ! Ended ()) { // Move back to allow for a pattern match over the previous buffer end. location = Math.max (End_Location () - pattern.length () + 1, 0); Extend (); } return (location < Buffer_Location) ? -1 : location; } /** Finds the next location of the character in the virtual stream.

Starting at the specified location find the next location of the specified character.

The character buffer will be extended until the specified character is found. Note: The character buffer will be extended to include all available input if the character can not be found.

@param location The location from which to start the search. @param character The character to be found. @return The location of the next occurance of the character in the virtual stream. If the end of input data is reached without finding the character, then -1 is returned. @throws IndexOutOfBoundsException The location is before the Buffer_Location. @throws IOException While reading characters to extend the buffer. */ public long Location_Of ( long location, char character ) throws IndexOutOfBoundsException, IOException { while ((location = Location (index_of (get_index (location), character))) < Buffer_Location && ! Ended ()) location = End_Location (); return (location < Buffer_Location) ? -1 : location; } /** Tests if the pattern equals the substring starting at the location.

The character buffer will be extended as needed to include a substring starting at the specified location that is as long as the pattern String.

@param location The location of the substring to compare. @param pattern The String to compare against the substring. @return true if the substring equals the pattern String; false otherwise. @throws IndexOutOfBoundsException The location is before the Buffer_Location. @throws IOException While reading characters to extend the buffer. */ public boolean Equals ( long location, String pattern ) throws IndexOutOfBoundsException, IOException { // Make sure the pattern doesn't overlap the end of the buffer. long end_of_pattern = location + pattern.length () - 1; while (end_of_pattern >= End_Location () && Extend ()); if (end_of_pattern >= End_Location ()) return false; return equals (get_index (location), pattern); } //------------------------------------------------------------------------------ /** Gets the index in the buffer for the location.

Makes sure the location has been read into the buffer. A location at or beyond the end of data input is set to the end location.

Note: As a special case a location of -1 is taken to mean the location of the next input character (i.e. End_Location ()). This accommodates sequential searches on the buffer where the return value is -1 when the search fails.

@param location The virtual stream location to get into the buffer. @return The buffer index of the effective location. */ private int get_index ( long location ) throws IndexOutOfBoundsException, IOException { if (location < Buffer_Location) throw new IndexOutOfBoundsException (Exception_Message ( "String_Buffer_Reader: Can't get to location " + location + " with the String_Buffer located at " + Buffer_Location + ".", location )); if (location == -1) location = End_Location (); while (location >= End_Location () && Extend ()); if (location >= End_Location ()) return Index (End_Location ()); return Index (location); } /*------------------------------------------------------------------------------ Exception message */ /** Provide a standard exception message.

The first line of the result message will be the class (@link #ID}. The second line will be the user specified message. The third line will be "At data input location .", where is the value of the specified location.

@param message The user message String. @param location The virtual stream location where the exception occurred. @return A standard exception message String. */ private static String Exception_Message ( String message, long location ) { return ID + NL + message + NL + "At data input location " + location + "."; } } // End of class pirl-2.3.8/PIRL/Strings/strip_DEBUG.java0000644000175000017500000002451611742734410017470 0ustar mathieumathieu/* strip_DEBUG PIRL CVS ID: strip_DEBUG.java,v 1.13 2012/04/16 06:15:36 castalia Exp Copyright (C) 2002-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Strings; import java.lang.String; import java.io.InputStream; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.FileNotFoundException; import java.io.StringWriter; import java.io.IOException; /** strip_DEBUG is a filter utility to remove "DEBUG" sections from source code files.

Use:

	java strip_DEBUG [<source filename>]

Since Java does not provide preprocessor capabilities, debugging code sections can not be surrounded by #ifdef statements as they are in C/C++. For Java, then, the in-line code debugging technique assumed here is the use of a "DEBUG" marker to identify sequences of code that are only used for debugging purposes. This utility strips "DEBUG" sequences from a source code file.

A "DEBUG" sequence is identified by an expression containing the "DEBUG" marker. All source code lines containing the expression are stripped from the resultant output. The expression may be may be a variable or variable list which includes all members of the list, a method which includes the entire method definition, a conditional which includes the expression or expression block following the conditional, or any other expression. When expressions are stripped the entirertiy of all lines containing the expression are removed; thus "DEBUG" expressions must not be mixed with other expressions on the same line(s).

N.B.: The "DEBUG" string that is immediately preceded by a whitespace character, end of line sequence or left parenthesis character is taken to be contained in a "DEBUG" expression. Thus comments containing the unquoted word "DEBUG" will be mistaken for part of an expression resulting in incorrectly stripping the line on which "DEBUG" occurs and the expression following the comment (unless the commend contains characters that can misinterpreted as the end of an expression). The "DEBUG"

The sripped file is written to stdout. If no source filename is specified, it is read from stdin.

@author Bradford Castalia, UA/PIRL @version 1.13 */ public class strip_DEBUG { public static final String ID = "PIRL.Strings.strip_DEBUG (1.13 2012/04/16 06:15:36)"; private static StringBuffer Source; private static final String WHITESPACE = " \t\n\r", EOL = "\n\r"; private static final int DEBUG_OFF = 0, DEBUG_STRIP = 1 << 0, DEBUG_BEGIN = 1 << 1, DEBUG_END = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; public static void main (String[] arguments) { // Read in the entire file into the Source. InputStreamReader source = null; if (arguments.length == 0) source = new InputStreamReader (System.in); else { try {source = new InputStreamReader (new FileInputStream (arguments[0]));} catch (FileNotFoundException exception) { System.err.println ("strip_DEBUG: Unable to access file " + arguments[0] + '\n' + exception.getMessage ()); System.exit (1); } } try { StringWriter source_writer = new StringWriter (); char characters[] = new char[1024]; int count; while ((count = source.read (characters, 0, 1024)) != -1) source_writer.write (characters, 0, count); Source = source_writer.getBuffer (); } catch (IOException exception) { System.err.println ("strip_DEBUG: Unable to read file " + arguments[0] + '\n' + exception.getMessage ()); System.exit (-1); } char character; int index = 0, begin, end; while ((index = Source.indexOf ("DEBUG", index)) >= 0) { if (index > 0) { character = Source.charAt (index - 1); if (character != '(' && character != ' ' && character != '\t' && character != '\n' && character != '\r') { index += 5; continue; } } begin = Expression_Begin_Line (index); end = Expression_End_Line (index); // Delete the sequence from the Source buffer. if ((DEBUG & DEBUG_STRIP) != 0) { String sequence = DEBUG_sequence (begin, end); System.err.println ("Deleting sequence from " + begin + " to " + end + '\n' + sequence); } Source.delete (begin, end); index = begin; } // Write out the stripped Source buffer. System.out.print (Source); System.exit (0); } private static String DEBUG_sequence ( int start, int end ) { return Source.substring (start, end); } /** Find the beginning of the line in which the expression containing the specified index starts.

Starting with the character immediately preceeding the specified index a search is made for the beginning of the line in containing the beginning of the expression in which the specified index is located. The specified index is expected to be located within an expression such that non-whitespace characters of the expression will preceed the index location. The Source is searched backwards from the location immediately preceeding the specified index for any non-whitespace character. Whitespace characters are ' ', '\t', '\n' or '\r'. Then the search continues for the end of the last line. The index of the character immediately following the last end of line sequence character, or zero if no end of line is found, is returned. End of line characters are '\n' or '\r'.

@param index The character index in the Source before which to begin the search. @return The character index in the Source for the beginning of the line in which the beginning of the expression containing the index location is located. */ private static int Expression_Begin_Line ( int index ) { if ((DEBUG & DEBUG_BEGIN) != 0) System.err.println ("Searching for beginning of expression beginning of line from index " + index); char character; // Search for non-whitespace character. while (--index > 0) { character = Source.charAt (index); if ((DEBUG & DEBUG_BEGIN) != 0) System.err.print (character); if (character == ' ' || character == '\t' || character == '\n' || character == '\r') continue; break; } // Search for the end of the last line. while (--index > 0) { character = Source.charAt (index); if ((DEBUG & DEBUG_BEGIN) != 0) System.err.print (character); if (character == '\n' || character == '\r') { // Return the beginning of the current line. if ((DEBUG & DEBUG_BEGIN) != 0) System.err.println ("Beginning of expression beginning of line: " + (index + 1)); return ++index; } } if ((DEBUG & DEBUG_BEGIN) != 0) System.err.println ("Beginning of expression beginning of line: 0"); return 0; } /** Find the end of the line in which the expression containing the specified index ends.

Starting with the character at the specified index a search is made for the end of the expression containing the specified index. The end of the expression is a semicolon character (';'). However, if an open brace character ('{') is seen first then the end of the expression is the matching close brace character ('}') with nested brace blocks skipped (they are part of the expression). During the search for the end of the expression characters preceeded by a backslash character ('\') and single or double quoted strings are skipped.

After the end of the expression is located a search is made for the next end of line sequence. The location immediately following this end of line sequence is returned.

@param index The character index in the Source after which to begin the search. @return The character index in the Source for the end of the line in which the end of expression containing the index location is located. */ private static int Expression_End_Line ( int index ) { if ((DEBUG & DEBUG_END) != 0) System.err.println ("Searching for end of expression end of line from index " + index); char character; int level = 0; while (index < Source.length ()) { character = Source.charAt (index++); if ((DEBUG & DEBUG_END) != 0) System.err.print (character); if (character == '\\') { // Escaped character. if ((DEBUG & DEBUG_END) != 0) System.err.print ("[Escape]" + Source.charAt (index)); index++; } else if (character == '"' || character == '\'') { // Skip quoted sequence. if ((DEBUG & DEBUG_END) != 0) System.err.println ("" + Source.charAt (index)); index++; } else if (character == '{') level++; else if (character == '}') { if (--level < 0) { System.err.println ("strip_DEBUG: Unbalanced braces in at location " + --index); System.exit (2); } if (level == 0) // End of block. break; } else if (level == 0 && character == ';') // End of statement. break; } if ((DEBUG & DEBUG_END) != 0) System.err.println ("\nExpression ends at index " + index); // Search for the end of the current line. while (index < Source.length ()) { character = Source.charAt (index++); if (character == '\n' || character == '\r') { // Skip the EOL sequence. if ((character == '\r' && Source.charAt (index) == '\n') || (character == '\n' && Source.charAt (index) == '\r')) ++index; break; } } if ((DEBUG & DEBUG_END) != 0) System.err.println ("End of Expression end of line: " + index); return index; } } // End of class pirl-2.3.8/PIRL/Strings/Words.java0000644000175000017500000005334711742734410016523 0ustar mathieumathieu/* Words PIRL CVS ID: Words.java,v 1.24 2012/04/16 06:15:36 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Strings; import java.util.Vector; import java.lang.IndexOutOfBoundsException; /** The Words class provides a mechanism to treat a String as a sequence of delimited words.

@see String_Buffer @author Bradford Castalia, UA/PIRL @version 1.24 */ public class Words { public static final String ID = "PIRL.Strings.Words (1.24 2012/04/16 06:15:36)"; /** The index in the current string where the next word starts.

Initially, this is the beginning (0) of the words string. This will be -1 when there are no more words. */ public int Start_Index = 0; /** The index (exclusive) in the current string where the current word ends.

This will be zero if no {@link #Next_Word() Next_Word} has been selected. This will be -1 when there are no more words. */ public int End_Index = 0; /** The Word_Index the last {@link #Mark() marked} word. */ public Word_Index Mark_Index = new Word_Index (); /** The default word delimiters;

The usual whitespace characters: " \n\r\t". */ public static final String DEFAULT_DELIMITERS = " \n\r\t"; /** The default word {@link #Mask(String) mask}. */ public static final String DEFAULT_MASK = "*******"; private String Delimiters = DEFAULT_DELIMITERS, Word_Mask = DEFAULT_MASK; private String_Buffer Characters = new String_Buffer (); /** Whether or not to treat quoted strings as a word. */ public static boolean QUOTED_WORDS = true; private boolean Quoted_Words = QUOTED_WORDS; /** Whether or not to delimit words at quotes. */ public static boolean DELIMIT_AT_QUOTE = true; private boolean Delimit_at_Quote = DELIMIT_AT_QUOTE; /** Whether or not to treat parenthesized strings as a word. */ public static boolean PARENTHESIZED_WORDS = false; private boolean Parenthesized_Words = PARENTHESIZED_WORDS; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_METHODS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs Words from a String of characters.

@param characters The String of characters containing words. */ public Words ( String characters ) {Characters = new String_Buffer (characters);} /** Constructs Words with no characters.

@see #Characters(String) */ public Words () {} /*============================================================================== Accessors */ /** Gets the Words characters.

@return The current String of characters. */ public String toString () {return Characters.toString ();} /** Sets the String of characters.

The current location is reset to the beginning of the string. The {@link #Mark_Index} is reset to (0,0).

@param characters The String of characters. @return This Words object. @see #Location(int) */ public Words Characters ( String characters ) { Characters = new String_Buffer (characters); Mark_Index = new Word_Index (); return Location (0); } /** Sets the word delimiter characters.

A word is delimited by a contiguous sequence of characters that are all members of the delimiters characters. Note that a sequence of more than one the same or different characters from the delimiters set does not result in empty words; i.e. any continguous sequence of one or more delimiters is treated as a single word delimiter.

N.B.: Any character starting a special sequence should not be included as one of the delimiter characters. If they are then special sequence recognition will be effectively disabled.

@param delimiters The String of delimiter characters. If null, the {@link #DEFAULT_DELIMITERS} will be used. @return This Words object. @see #Quoted_Words(boolean) @see #Parenthesized_Words(boolean) */ public Words Delimiters ( String delimiters ) { if (delimiters == null) delimiters = DEFAULT_DELIMITERS; Delimiters = delimiters; return this; } /** Gets the current delimiters.

@return The String of delimiter characters. */ public String Delimiters () {return Delimiters;} /** Enable or disable the treatment of quoted strings as words.

The enclosing quote characters are included in the word. If there is no matching unescaped quote character before the end of the string, the resulting word will not have the matching closing quote character at {@link #End_Index} - 1.

@param enable true if all characters within unescaped quotes (' or ") are to be treated as a single word; false otherwise. @return This Words object. @see #Delimit_at_Quote(boolean) @see #Next_Location() */ public Words Quoted_Words ( boolean enable ) {Quoted_Words = enable; return this;} /** Test if quoted strings are treated as single words.

@return true if all characters within unescaped quotes (' or ") will be treated as a single word; false otherwise. @see #Quoted_Words(boolean) */ public boolean Quoted_Words () {return Quoted_Words;} /** Enable or disable delimiting words at quotes.

When unescaped quote characters are encountered they may delimit a word even if no {@link #Delimiters(String) delimiter character} preceeds or follows the quote. Disabling quote delimiting causes contiguous non-delimiter characters to be included as part of the quoted string word. The quotes remain in the word in either case.

@param enable true if unescaped quote characters delimit a word; false otherwise. @return This Words object. @see #Quoted_Words(boolean) */ public Words Delimit_at_Quote ( boolean enable ) {Delimit_at_Quote = enable; return this;} /** Test if quotes will delimit words.

@return true if unescaped quote characters delimit a word; false otherwise. @see #Delimit_at_Quote(boolean) */ public boolean Delimit_at_Quote () {return Delimit_at_Quote;} /** Enable or disable the treatment of parenthesized strings as words.

N.B.: Nested parenthesized strings are included in a parenthesized string.

The enclosing parentheses characters are included in the word. If there is no matching unescaped closing parenthesis character (ignoring nested parentheses) before the end of the string, the resulting word will not have one at {@link #End_Index} - 1.

@param enable true if all characters within unescaped parenthesized ('(' and ')') strings are to be treated as a single word; false otherwise. @return This Words object. @see #Next_Location() */ public Words Parenthesized_Words ( boolean enable ) {Parenthesized_Words = enable; return this;} /** Test if parenthesized strings are treated as single words.

@return true if all characters within unescaped parenthesized strings will be treated as a single word; false otherwise. @see #Parenthesized_Words(boolean) */ public boolean Parenthesized_Words () {return Parenthesized_Words;} /** Gets a substring of the characters.

@param start The start index of the substring. @param end The end index of the substring. @return The substring from the start index up to, but not including, the end index. @see StringBuffer#substring(int, int) */ public String Substring ( int start, int end ) {return Characters.substring (start, end);} /** Gets a substring of the characters.

@param start The start index of the substring. @return The substring from the start index to the end of the characters string. @see StringBuffer#substring(int) */ public String Substring ( int start ) {return Characters.substring (start);} /** Gets a substring of the characters.

@param word_index A Word_Index for the substring. @return The substring from the word_index.Start_Index up to, but not including, the word_index.End_Index. @see StringBuffer#substring(int, int) */ public String Substring ( Word_Index word_index ) {return Characters.substring (word_index.Start_Index, word_index.End_Index);} /*============================================================================== Word_Index */ /** A Word_Index provides a start,end string location for a word. */ public class Word_Index { /** A string index where a word starts. */ public int Start_Index; /** A string index where a word ends. */ public int End_Index; /** Constructs a Word_Index at (0,0). */ public Word_Index () { Start_Index = End_Index = 0; } /** Constructs a Word_Index for start and end word locations. */ public Word_Index ( int start, int end ) { Start_Index = start; End_Index = end; } } /*============================================================================== Manipulators */ /** Moves the word indices to a new location.

The location must be within the words string.

@param location An index in the words string. @return This Words object. @throws IndexOutOfBoundsException If the location is not within the words string. */ public Words Location ( int location ) throws IndexOutOfBoundsException { if (location < 0 || location > Characters.length ()) throw new IndexOutOfBoundsException (ID + '\n' + "Location " + location + " is outside the words string length of " + Characters.length ()); Start_Index = End_Index = location; return this; } /** Marks the current word location.

The current {@link #Start_Index} and {@link #End_Index} are stored in the {@link #Mark_Index}.

@return This Words object. @see #Restore() */ public Words Mark () { Mark_Index = new Word_Index (Start_Index, End_Index); return this; } /** Restores the current word to the last marked location.

@return This Words object. @throws StringIndexOutOfBoundsException If the Word_Index.Start_Index is less than zero or the Word_Index.End_Index is greater than the number of characters available. @see #Mark() */ public Words Restore () { if (Mark_Index.Start_Index < 0 || Mark_Index.End_Index > Characters.length ()) throw new StringIndexOutOfBoundsException (ID + '\n' + "Can't restore the invalid Mark_Index (" + Mark_Index.Start_Index + ", " + Mark_Index.End_Index); Start_Index = Mark_Index.Start_Index; End_Index = Mark_Index.End_Index; return this; } /** Moves the word indices to the location of the next word.

Beginning at the current {@link #End_Index} all {@link #Delimiters(String) delimiter characters} are skipped to find the new {@link #Start_Index}. If the end of the characters string is reached without finding a non-delimiter character then there are no more words available. In this case both the Start_Index and End_Index will be equal to the character string length and nothing more will be done.

N.B.: Any character starting a special sequence should not be included as one of the delimiter characters. If they are then special sequence recognition will be effectively disabled.

The character at the Start_Index is checked to see if it starts a special sequence. If {@link #Quoted_Words(boolean) quoted words} is enabled either a single (') or double (") quote character will be recognized and set as the end of sequence marker character. If {@link #Parenthesized_Words(boolean) parenthesized words} is enabled an opening parenthesis ('(') character will be recognized and the end of sequence marker character will be set to the closing parenthesis (')') character. A special sequence start character is included as part of the word.

When {@link #Delimit_at_Quote(boolean) delimit at quote} is enabled in addition to quoted words being enabled quoted strings are delimited as separate words even if a contiguous non-delimiter character preceeds and/or follows the enclosing quotes. When delimit at quote is disabled the contiguous non-delimiter characters are treated as part of the word that includes the quoted string.

The word contains all characters up to and including an unescaped end of sequence marker character. For a parenthesized sequence the marker character must be at parenthesis level zero to end the sequence; unescaped nested parentheses increase the parenthesis level. Note that a special sequence may include what would otherwise be considered delimiter characters, and the enclosing characters - quotes or parentheses - are included as part of the word. If the end of the characters string is reached before the expected marker character is found the resulting word will be "unbalanced"; the character at End_Index - 1 will not be the marker character.

If no end of sequence marker character has been set, then the word will end when any unescaped delimiter character is found or the end of the characters string is reached. If quoted words are enabled a quote character will be recognized as a delimiter character. If parenthesized words are enabled an opening parenthesis character will be recognized as a delimiter character. The index of the delimiter character becomes the new End_Index; it is not included as part of the word.

Any character preceded by a backslash ('\') character is escaped from any special treatment. All escaped characters are taken to be part of the word, the backslash character included.

@return A Word_Index for the next word. If there are no more words the word index will be set to the end of the characters. */ public Word_Index Next_Location () { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (">>> Words.Next_Location"); if ((Start_Index = Characters.skip_over (End_Index, Delimiters)) < Characters.length ()) { // A non-delimiter character following the current word end index. if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" Start_Index = " + Start_Index); // Initialize the new end-of-word index to the start index. End_Index = Start_Index; // Check for the start of a special sequence. int length = Characters.length (), level = 0; // Parentheses nesting level. char marker = 0, // Special sequence end marker character. character = Characters.charAt (Start_Index); if (Quoted_Words && (character == '"' || character == '\'')) { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" Quoted with " + character); marker = character; ++End_Index; } else if (Parenthesized_Words && character == '(') { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" Parenthesized"); marker = ')'; ++level; ++End_Index; } // Search for the next end-of-word marker. Search: while (End_Index < length) { character = Characters.charAt (End_Index); if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" " + End_Index + ": '" + character + '\''); if (character == '\\') { // Skip the escaped character. if (++End_Index == length) break; } else if (marker == 0) { // No special sequence identification in effect. if (Delimiters.indexOf (character) >= 0) // Word delimiter found. break; if (Quoted_Words && (character == '"' || character == '\'')) { // Start of a contiguous quoted sequence. if (Delimit_at_Quote) break; else { // Search for the end of the sequence. char quote = character; while (++End_Index < length) { character = Characters.charAt (End_Index); if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" " + End_Index + ": '" + character + '\''); if (character == quote) { ++End_Index; break; } else if (character == '\\') { // Skip the escaped character. if (++End_Index == length) break Search; } } continue; } } if (Parenthesized_Words && character == '(') // Start of a contiguous parenthesized sequence. break; } else if (character == marker) { ++End_Index; // Always include the marker in the word. if (level != 0 && --level != 0) // End of nested parenthesized sequence. continue; // End of special sequence. break; } else if (Parenthesized_Words && character == '(') // Start of nested parenthesized sequence. ++level; // Word character. ++End_Index; } } else // No more words following the current word. End_Index = Start_Index; if ((DEBUG & DEBUG_METHODS) != 0) System.out.println ("<<< Words.Next_Location: " + Start_Index + ", " + End_Index); return new Word_Index (Start_Index, End_Index); } /** Gets the next word.

The Start_Index will be moved forward from the current End_Index over any Delimiters. Then the End_Index will be moved forward from the Start_Index until any Delimiters are found or the end of the string is reached.

@return The substring from the next Start_Index up to, but not including, the next End_Index. If there are no more words, the empty String will be returned. @see #Next_Location() */ public String Next_Word () {return Substring (Next_Location ());} /** Splits the remaining characters into words.

Beginning with the {@link #Next_Word() next word}, words are collected into a Vector in the order they occur in the string.

If the limit is 0 all available words will be returned; no delimiters will be included in any word that is returned. If the limit is positive (> 0) no more than limit words will be returned; the last "word" will contain all characters, including any delimiters, following the start of the last word (delimiters preceeding the last word will not be included). A negative limit acts the same as a positive limit except the last "word" will contain all characters following the end of the previous word (delimiters preceeding the last word will be included). Note that a limit of -1 will return all characters from the current End_Index to the end of the the characters string.

Less than limit words my be returned. No empty words will be returned.

@param limit The word limit to return. @return A Vector of zero or more words. @see #Next_Location() */ public Vector Split ( int limit ) { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (">>> Words.Split: " + limit); Vector words = new Vector (); int total = (limit < 0) ? -limit : limit; while (total-- != 1) { Next_Location (); if (Start_Index < End_Index) words.add (Substring (Start_Index, End_Index)); else // No more words. break; } if (Start_Index < End_Index && total >= 0) { if (limit > 0) { // Advance to the next word. Next_Location (); if (Start_Index < End_Index) words.add (Substring (Start_Index)); } else if (Start_Index < End_Index) words.add (Substring (End_Index)); } if ((DEBUG & DEBUG_METHODS) != 0) System.out.println ("<<< Words.Split: " + words); return words; } /** Splits the remaining characters into words.

Beginning with the {@link #Next_Word() next word}, words are collected into a Vector in the order they occur in the string.

@return A Vector of zero or more words. @see #Split(int) */ public Vector Split () {return Split (0);} /** Sets the mask to use when words are masked.

@param mask The mask String. This may be null. @return This Words object. @see #Mask(Vector) */ public Words Mask ( String mask ) {Word_Mask = mask; return this;} /** Gets the word mask.

@return The String to me used when masking words. @see #Mask(Vector) */ public String Mask () {return Word_Mask;} /** Words preceeded by any one of a set of names are masked.

The words are searched for matches with the names. When a match is found, the following word is replaced with the mask String. If the mask String is null the preceeding name as well its word is deleted.

N.B.: The mask string may be one of the names. The mask substitution is never compared against the names list.

@param names A Vector of names to find. @return This Words object. @see #Mask(String) @see #Next_Word() */ public Words Mask ( Vector names ) { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (">>> Words.Mask:\n" + names + '\n' + Characters); int mask_length = (Word_Mask == null) ? 0 : Word_Mask.length (); String word; while ((word = Next_Word ()).length () != 0) { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" + Start_Index = " + Start_Index + ", End_Index = " + End_Index); if (names.contains (word)) { if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" Found: " + word); int // Drop the name along with its word. start = Start_Index; Next_Word (); if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" Next_Word: " + Characters.substring (Start_Index, End_Index)); if (Word_Mask == null) Characters.delete (start, End_Index); else if ((start = Start_Index) != End_Index) Characters.replace (Start_Index, End_Index, Word_Mask); // Adjust the End_Index for the mask replacement. End_Index -= End_Index - start - mask_length; // Advance over the replacement section. Start_Index = End_Index; if ((DEBUG & DEBUG_METHODS) != 0) System.out.println (" - Start_Index = " + Start_Index + ", End_Index = " + End_Index); } } if ((DEBUG & DEBUG_METHODS) != 0) System.out.println ("<<< Words.Mask:\n" + Characters); return this; } } // End of Words class. pirl-2.3.8/PIRL/Strings/String_Buffer.java0000644000175000017500000007341211742734407020165 0ustar mathieumathieu/* String_Buffer PIRL CVS ID: String_Buffer.java,v 1.21 2012/04/16 06:15:35 castalia Exp Copyright (C) 2001-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Strings; import java.lang.StringBuffer; /** A String_Buffer is a wrapper for the StringBuffer class.

The StringBuffer class is declared final so it can not be extended. The String_Buffer class implements all of the StringBuffer methods (by simply calling the corresponding method) plus numerous methods that provide extended and convenient capabilities along the usual lines of String manipulation. The wrapped methods all function identically to their StringBuffer counterparts, of course, except when a reference to this StringBuffer object is returned it is a reference to this String_Buffer object.

@see StringBuffer @author Bradford Castalia, UA/PIRL @version 1.21 */ public class String_Buffer { public static final String ID = "PIRL.Strings.String_Buffer (1.21 2012/04/16 06:15:35)"; private StringBuffer _StringBuffer_; /*============================================================================== Constants */ public static final String NL = System.getProperty ("line.separator"); public static final char QUESTIONABLE_CHARACTER = '?'; /*============================================================================== Constructors */ /** Constructs a String_Buffer with no characters in it and an initial capacity of 16 characters. */ public String_Buffer () {_StringBuffer_ = new StringBuffer ();} /** Constructs a String_Buffer with no characters in it and an initial capacity specified by the length argument.

@param length The initial capacity. @throws NegativeArraySizeException If the length argument is less than 0. */ public String_Buffer (int length) {_StringBuffer_ = new StringBuffer (length);} /** Constructs a String_Buffer so that it represents the same sequence of characters as the string argument; in other words, the initial contents of the String_Buffer is a copy of the argument string. The initial capacity of the String_Buffer is 16 plus the length of the string argument.

@param string The initial contents of the buffer. */ public String_Buffer (String string) {_StringBuffer_ = new StringBuffer (string);} /*============================================================================== StringBuffer Methods (wrapped) */ /** @see StringBuffer#append(boolean) */ public String_Buffer append (boolean b) {_StringBuffer_.append (b); return this;} /** @see StringBuffer#append(char) */ public String_Buffer append (char c) {_StringBuffer_.append (c); return this;} /** Append a byte as a character.

@param b The byte to be appended. */ public String_Buffer append (byte b) {return append ((char)b & 0xFF);} /** @see StringBuffer#append(char[]) */ public String_Buffer append (char[] s) {_StringBuffer_.append (s); return this;} /** Append a byte array as characters.

Equivalent to append (bytes, 0, bytes.length);

@param bytes An array of bytes. */ public String_Buffer append (byte[] bytes) {return append (bytes, 0, bytes.length);} /** @see StringBuffer#append(char[], int, int) */ public String_Buffer append (char[] s, int offset, int length) {_StringBuffer_.append (s, offset, length); return this;} /** Append a set of bytes as characters.

A convenience method for append (new String (bytes, offset, length));

@param bytes An array of bytes. @param offset The byte array offset of the first byte to be appended. @param length The number of bytes to be appended. @throws IndexOutOfBoundsException If the offset and/or length index bytes outside array bounds. */ public String_Buffer append (byte[] bytes, int offset, int length) throws IndexOutOfBoundsException { if (bytes != null) append (new String (bytes, offset, length)); return this; } /** @see StringBuffer#append(double) */ public String_Buffer append (double d) {_StringBuffer_.append (d); return this;} /** @see StringBuffer#append(float) */ public String_Buffer append (float f) {_StringBuffer_.append (f); return this;} /** @see StringBuffer#append(int) */ public String_Buffer append (int i) {_StringBuffer_.append (i); return this;} /** @see StringBuffer#append(long) */ public String_Buffer append (long l) {_StringBuffer_.append (l); return this;} /** @see StringBuffer#append(Object) */ public String_Buffer append (Object o) {_StringBuffer_.append (o); return this;} /** @see StringBuffer#append(String) */ public String_Buffer append (String s) {_StringBuffer_.append (s); return this;} /** @see StringBuffer#capacity() */ public int capacity () {return _StringBuffer_.capacity ();} /** @see StringBuffer#charAt(int) */ public char charAt (int index) {return _StringBuffer_.charAt (index);} /** @see StringBuffer#delete(int, int) */ public String_Buffer delete (int start, int end) {_StringBuffer_.delete (start, end); return this;} /** @see StringBuffer#deleteCharAt(int) */ public String_Buffer deleteCharAt (int index) {_StringBuffer_.deleteCharAt (index); return this;} /** @see StringBuffer#ensureCapacity(int) */ public void ensureCapacity (int minimumCapacity) {_StringBuffer_.ensureCapacity (minimumCapacity);} /** @see StringBuffer#getChars(int, int, char[], int) */ public void getChars (int srcBegin, int srcEnd, char[] dst, int dstBegin) {_StringBuffer_.getChars (srcBegin, srcEnd, dst, dstBegin);} /** @see StringBuffer#insert(int, boolean) */ public String_Buffer insert (int index, boolean b) {_StringBuffer_.insert (index, b); return this;} /** @see StringBuffer#insert(int, char) */ public String_Buffer insert (int index, char c) {_StringBuffer_.insert (index, c); return this;} /** @see StringBuffer#insert(int, char[]) */ public String_Buffer insert (int index, char[] s) {_StringBuffer_.insert (index, s); return this;} /** @see StringBuffer#insert(int, char[], int, int) */ public String_Buffer insert (int index, char[] s, int offset, int length) {_StringBuffer_.insert (index, s, offset, length); return this;} /** @see StringBuffer#insert(int, double) */ public String_Buffer insert (int index, double d) {_StringBuffer_.insert (index, d); return this;} /** @see StringBuffer#insert(int, float) */ public String_Buffer insert (int index, float f) {_StringBuffer_.insert (index, f); return this;} /** @see StringBuffer#insert(int, int) */ public String_Buffer insert (int index, int i) {_StringBuffer_.insert (index, i); return this;} /** @see StringBuffer#insert(int, long) */ public String_Buffer insert (int index, long l) {_StringBuffer_.insert (index, l); return this;} /** @see StringBuffer#insert(int, Object) */ public String_Buffer insert (int index, Object o) {_StringBuffer_.insert (index, o); return this;} /** @see StringBuffer#insert(int, String) */ public String_Buffer insert (int index, String s) {_StringBuffer_.insert (index, s); return this;} /** @see StringBuffer#length() */ public int length () {return _StringBuffer_.length ();} /** @see StringBuffer#replace(int, int, String) */ public String_Buffer replace (int start, int end, String string) {_StringBuffer_.replace (start, end, string); return this;} /** @see StringBuffer#reverse() */ public String_Buffer reverse () {_StringBuffer_.reverse (); return this;} /** @see StringBuffer#setCharAt(int, char) */ public void setCharAt (int index, char c) {_StringBuffer_.setCharAt (index, c);} /** @see StringBuffer#setLength(int) */ public void setLength (int length) {_StringBuffer_.setLength (length);} /** @see StringBuffer#substring(int) */ public String substring (int index) {return _StringBuffer_.substring (index);} /** @see StringBuffer#substring(int, int) */ public String substring (int start, int end) {return _StringBuffer_.substring (start, end);} /** @see StringBuffer#toString() */ public String toString () {return _StringBuffer_.toString ();} /*============================================================================== Extension Methods */ /** Skips over any and all characters in the skip String, starting with the character at the specified index.

@param index The index of the first character to test. @param skip The set of characters to be skipped. @return The index of the character at or beyond the starting index that is not in the skip String, or the end (length) index if all remaining characters from the starting index are in the skip String. @see String#indexOf(int) @see String_Buffer#charAt(int) */ public int skip_over ( int index, String skip ) { int end = length (); if (index < 0 || index >= end) return end; while (index < end) if (skip.indexOf (charAt (index++)) < 0) // The character isn't in the skip string. return --index; return index; } /** @see #skip_over(int, String) */ public int skipOver (int index, String skip) {return skip_over (index, skip);} /** Skips back over any and all characters in the skip String, starting with the character at the specified index.

@param index The index of the first character to test. @param skip The set of characters to be skipped. @return The index of the last character at or before the starting index that is not in the skip String, or -1 if all characters up to and including the starting index are in the skip String. @see String#indexOf(int) @see String_Buffer#charAt(int) */ public int skip_back_over ( int index, String skip ) { if (index >= length ()) index = length () - 1; while (index >= 0) if (skip.indexOf (charAt (index--)) < 0) // The character isn't in the skip string. return ++index; return index; } /** @see #skip_back_over(int, String) */ public int skipBackOver (int index, String skip) {return skip_back_over (index, skip);} /** Skips until a character also in the find String is found, starting with the character at the specified index.

@param index The index of the first character to test. @param find The set of potential characters to find. @return The index of the character at or beyond the starting index that is in the find String, or -1 if all remaining characters from the starting index are not in the find String. @see String#indexOf(int) @see String_Buffer#charAt(int) */ public int skip_until ( int index, String find ) { int end = length (); if (index < 0) return -1; while (index < end) if (find.indexOf (charAt (index++)) >= 0) // The character is in the find string. return --index; return -1; } /** @see #skip_until(int, String) */ public int skipUntil (int index, String find) {return skip_until (index, find);} /** Skips back until a character also in the find String is found, starting with the character at the specified index.

@param index The index of the first character to test. @param find The set of potential characters to find. @return The index of the last character at or before the starting index that is in the find String, or -1 if all characters up to and including the starting index are not in the find String. @see String#indexOf(int) @see String_Buffer#charAt(int) */ public int skip_back_until ( int index, String find ) { if (index >= length ()) index = length () - 1; while (index >= 0) if (find.indexOf (charAt (index--)) >= 0) // The character is in the find string. return ++index; return index; } /** @see #skip_back_until(int, String) */ public int skipBackUntil (int index, String find) {return skip_until (index, find);} /** Gets the index of the next character matching the specified character, starting with the specified index.

@param index The index of the first character to test. @param character The character to match. @return The index of the next character at or beyond the starting index that matches the specified character, or -1 if all remaining characters do not match the specified character. @see String#indexOf(int, int) */ public int index_of ( int index, char character ) { int end = length (); if (index < 0) return -1; while (index < end) if (charAt (index++) == character) // Found the character. return --index; return -1; } /** @see #index_of(int, char) */ public int indexOf (int index, char character) {return index_of (index, character);} /* Return the index of the pattern String starting from the specified index of the string buffer; or -1 if the pattern isn't found. */ /** Gets the index of the next occurance of the substring matching the pattern String, starting with the specified index.

@param index The starting index from which to search for the pattern String. @param pattern The pattern String to match. @return The index of the next substring at or beyond the starting index that matches the pattern String, or -1 if the pattern is not found. @see String#indexOf(String, int) */ public int index_of ( int index, String pattern ) { try {return toString ().indexOf (pattern, index);} catch (Exception exception) {return -1;} } /** @see #index_of(int, String) */ public int indexOf (int index, String pattern) {return index_of (index, pattern);} /*------------------------------------------------------------------------------ */ /** Tests if the pattern String is equal to the substring of the same length starting at the index.

@param index The starting index from which to test for the pattern String. @param pattern The pattern String to match. @return true if they are equal; false otherwise. @see String#startsWith(String, int) */ public boolean equals ( int index, String pattern ) { try {return toString ().startsWith (pattern, index);} catch (Exception exception) {return false;} } /** Tests if the pattern String is equal regardless of case to the substring of the same length starting at the index.

@param index The starting index from which to test for the pattern String. @param pattern The pattern String to match. @return true if they are equal while ignoring case; false otherwise. @see String#regionMatches(boolean, int, String, int, int) */ public boolean equals_ignore_case ( int index, String pattern ) {return toString ().regionMatches (true, index, pattern, 0, pattern.length ());} /** @see #equals_ignore_case(int, String) */ public boolean equalsIgnoreCase (int index, String pattern) {return equals_ignore_case (index, pattern);} /*------------------------------------------------------------------------------ */ /** Replaces all occurances of the old_string with the new_string, starting at the specified index.

Recursive replacements are avoided: If the new_string contains the old_string, the new old_string remains in its location within the new_string; the search for an old_string continues at the location after where the new_string has been inserted.

@param index The starting index from which to test for the old_string. @param old_string The substring to find. @param new_string The String to replace the old_string. @return This, possibly modified, String_Buffer. @see #index_of(int, String) @see StringBuffer#replace(int, int, String) */ public String_Buffer replace ( int index, String old_string, String new_string ) { while ((index = index_of (index, old_string)) >= 0) { replace (index, index + old_string.length (), new_string); index += new_string.length (); } return this; } /** Replaces all sequences of any and all characters from the span String with the substitute String, starting at the specified index.

Each replacement occurs on the longest sequence of characters all from the span String set.

Recursive replacements are avoided: If the substitute contains any characters from the span the substitute remains unaffected; the search for another span continues at the location after where the substitute has been inserted.

@param index The starting index from which to test for span characters. @param span The set of potential characters for replacement. @param substitute The String to replace each span sequence. @return This, possibly modified, String_Buffer. @see #skip_until(int, String) @see #skip_over(int, String) @see StringBuffer#replace(int, int, String) */ public String_Buffer replace_span ( int index, String span, String substitute ) { while ((index = skip_until (index, span)) >= 0) { replace (index, skip_over (index, span), substitute); index += substitute.length (); } return this; } /** @see #replace_span(int, String, String) */ public String_Buffer replaceSpan (int index, String span, String substitute) {return replace_span (index, span, substitute);} /** Trims whitespace from both ends of a substring of the String_Buffer, starting at the start index and ending before the end index.

@param start The index of the starting character of the substring. @param end The index of the end (last + 1) character of the substring. @return This, possibly modified, String_Buffer. @see StringBuffer#substring(int, int) @see String#trim() @see StringBuffer#replace(int, int, String) */ public String_Buffer trim ( int start, int end ) {replace (start, end, substring (start, end).trim ()); return this;} /** Trims whitespace from both ends of the String_Buffer.

@return This, possibly modified, String_Buffer. @see #trim(int, int) */ public String_Buffer trim () {return trim (0, length ());} /** Trims whitespace from the beginning of the String_Buffer.

@return This, possibly modified, String_Buffer. @see Character#isWhitespace(char) @see #delete(int, int) */ public String_Buffer trim_beginning () { int index = 0, end = length (); while (index < end) if (! Character.isWhitespace (charAt (index++))) // Found a non-whitespace character. return delete (0, --index); return this; } /** Trims whitespace from the end of the String_Buffer.

@return This, possibly modified, String_Buffer. @see Character#isWhitespace(char) @see #delete(int, int) */ public String_Buffer trim_end () { int index = length (); while (--index >= 0) if (! Character.isWhitespace (charAt (index))) // Found a non-whitespace character. return delete (++index, length ()); return this; } /** Trims a character from both ends of a string.

@param character The character to be trimmed off the ends. @return This, possibly modified, String_Buffer. */ public String_Buffer trim ( char character ) { if (length () > 0 && charAt (0) == character) deleteCharAt (0); if (length () > 0 && charAt (length () - 1) == character) deleteCharAt (length () - 1); return this; } /** Trims a sequence of the same character from both ends of a string.

@param character The character to be trimmed off the ends. @return This, possibly modified, String_Buffer. */ public String_Buffer trim_all ( char character ) { int index = 0; for (index = 0; index < length (); index++) if (charAt (index) != character) break; delete (0, index); for (index = length (); --index >= 0;) if (charAt (index) != character) break; delete (++index, length ()); return this; } /** Empties the buffer of all characters.

@return This String_Buffer. */ public String_Buffer clear () {return delete (0, length ());} /** Clean all unescaped occurances of a character from a String.

Escaped characters, which are preceded by a backslash ('\') character, are always ignored. All other characters that match the character to be cleaned are removed from the String.

The backslash character can be cleaned from the String. Escaped backslash characters will result in a single backslash character. Thus a String might be first cleaned of a non-backslash character and then cleaned of backslash characters to remove any escape characters remaining from the previous cleaning.

@param character The char to be cleaned from the String. @return This, possibly modified, String_Buffer. @see #escape_to_special() */ public String_Buffer clean ( char character ) { for (int index = 0; index < length (); index++) { char it = charAt (index); if (it == character) { deleteCharAt (index); if (character != '\\') --index; } else if (it == '\\') ++index; } return this; } /** Substitutes escape sequences with special characters.

The following escape sequences, and their corresponding special characters, are recognized:

\b - Backspace (BS)
\t - Horizontal tab (HT)
\n - Newline (NL)
\f - Form feed (FF)
\r - Carriage return (CR)
\X - The character X
\0nnn - The character having the octal value nnn (0 <= nnn <= 177777)

The escape sequences will be substituted for their corresponding special characters, which will shorten the length of the String_Buffer. All backslash characters, except those that are themselves escaped, will be removed from the String_Buffer.

N.B.: If an octal valued escape sequence is immediately followed by an octal digit (0-7) the latter will be mistaken for part of the escape sequence unless the maximum 6 digit limit has been reached. Because of this it may be preferable to encode non-printable characters using standard {@link #to_character_references() character references}.

@return This String_Buffer. The conversion is done in-place. @see #special_to_escape() */ public String_Buffer escape_to_special () { int index; for (index = 0; index < (length () - 1); index++) { if (charAt (index) == '\\') { // Delete the backslash. deleteCharAt (index); // Check the escaped character. switch (charAt (index)) { case 'b': // BS setCharAt (index, '\b'); break; case 't': // HT setCharAt (index, '\t'); break; case 'n': // NL deleteCharAt (index); insert (index, NL); break; case 'f': // FF setCharAt (index, '\f'); break; case 'r': // CR setCharAt (index, '\r'); break; case '0': // Octal value. char character, new_character = 0; int end_index; for (end_index = index + 1; end_index < length () && (end_index - index) < 7; // No more that six digits. end_index++) { // Only octal digits are acceptable. if ((character = charAt (end_index)) > '7' || character < '0') break; new_character *= 8; new_character += character - 48; } if (new_character <= Character.MAX_VALUE) { setCharAt (index, new_character); delete (index + 1, end_index); } else // Skip invalid escape sequence. index = end_index - 1; break; } } } return this; } /** Substitutes escape sequences with special characters.

@param string The String to be filtered. If null, null is returned. @return The filtered String. @see #escape_to_special() */ public static String escape_to_special ( String string ) { if (string != null) string = new String_Buffer (string) .escape_to_special ().toString (); return string; } /** Substitutes special characters with escape sequences.

All control characters - less than ' ' (32) or greater than '~' (126) - will be substituted with escape sequences:

\b - for backspace (BS, 8)
\t - for horizontal tab (HT, 9)
\n - for newline (NL, 10)
\f - for form feed (FF, 12)
\r - for carriage return (CR, 13)
\0nnn - for all other special characters where nnn is the octal value of the character (1-6 digits).

The escape sequences will substitute for their corresponding special characters, which will increase the length of the String_Buffer.

N.B.: If an octal valued escape sequence is immediately followed by an octal digit (0-7) the latter will be mistaken for part of the escape sequence unless the maximum 6 digit limit has been reached. Because of this it may be preferable to encode non-printable characters using standard {@link #to_character_references() character references}.

@return This String_Buffer. The conversion is done in-place. @see #escape_to_special() */ public String_Buffer special_to_escape () { char character; String escape; int index = 0; while (index < length ()) { character = charAt (index); if (character < 32 || character > 126) { // Check the special character. switch (character) { case '\b': // BS escape = "b"; break; case '\t': // HT escape = "t"; break; case '\n': // NL escape = "n"; break; case '\f': // FF escape = "f"; break; case '\r': // CR escape = "r"; break; default: // Octal value. escape = "0" + Integer.toString ((int)character, 8); } setCharAt (index++, '\\'); insert (index, escape); index += escape.length (); } else index++; } return this; } /** Substitutes special characters with escape sequences.

@param string The String to be filtered. If null, null is returned. @return The filtered String. @see #special_to_escape() */ public static String special_to_escape ( String string ) { if (string != null) string = new String_Buffer (string) .special_to_escape ().toString (); return string; } /** Substitutes numeric character references with characters.

All numeric character references will be replaced with their single character value. A numeric character references has the form:

&#[X]N;

Where N is the text representation of the character value. If the 'X' (case insensitive) is present the value is in hexadecimal representation, otherwise it is decimal. N.B.: The semicolon terminating the numerical character reference must be present as well as the &# prefix that signals the presence of the reference.

@return This String_Buffer. The conversion is done in-place. @see #to_character_references() */ public String_Buffer from_character_references () { char previous = 0, character; int index = 0; while (index < length ()) { if ((character = charAt (index)) == '#' && previous == '&') { if (++index == length ()) break; int start = index - 2, begin = index, radix = 10; if ((character = charAt (index)) == 'X' || character == 'x') { if (++index == length ()) break; begin++; radix = 16; character = charAt (index); } while (Character.digit (character, radix) >= 0) { if (++index == length ()) break; character = charAt (index); } if (index > start) { try { int value = Integer.parseInt (substring (begin, index), radix); if (value <= Character.MAX_VALUE) { setCharAt (start, (char)value); delete (start + 1, ++index); index = start; } } catch (NumberFormatException exception) {} } } index++; previous = character; } return this; } /** Substitutes numeric character references with characters.

@param string The String to be filtered. If null, null is returned. @return The filtered String. @see #from_character_references() */ public static String from_character_references ( String string ) { if (string != null) string = new String_Buffer (string) .from_character_references ().toString (); return string; } /** Substitutes non-printable characters with numeric character references.

All non-printable characters - less than ' ' (32) or greater than '~' (126) - will be substituted with hexadecimal numeric character references. A hexadecimal numeric character references has the form:

&#XN;

Where N is the hexadecimal (uppercase) representation of the character value.

@return This String_Buffer. The conversion is done in-place. @see #from_character_references() @see #special_to_escape() */ public String_Buffer to_character_references () { char character; String reference; int index = 0; while (index < length ()) { character = charAt (index); if (character < 32 || character > 126) { reference = "#X" + Integer.toString ((int)character, 16).toUpperCase () + ';'; setCharAt (index++, '&'); insert (index, reference); index += reference.length (); } else index++; } return this; } /** Substitutes non-printable characters with numeric character references.

@param string The String to be filtered. If null, null is returned. @return The filtered String. @see #to_character_references() */ public static String to_character_references ( String string ) { if (string != null) string = new String_Buffer (string) .to_character_references ().toString (); return string; } /** Substitute all non-ASCII and unprintable characters with a question mark.

All non-printable characters - less than ' ' (32) or greater than '~' (126) - will be substituted with the {@link #QUESTIONABLE_CHARACTER question mark} ('?') character.

@return This String_Buffer. The conversion is done in-place. */ public String_Buffer to_printable_ASCII () { char character; int index = length (); while (index-- != 0) { character = charAt (index); if (character < 32 || character > 126) setCharAt (index, QUESTIONABLE_CHARACTER); } return this; } /** Substitute all non-ASCII and unprintable characters with a question mark.

@param string The string to be filtered. If null, null is returned. @return The filtered String. @see #to_printable_ASCII() */ public static String to_printable_ASCII ( String string ) { if (string != null) string = new String_Buffer (string) .to_printable_ASCII ().toString (); return string; } } // End of class pirl-2.3.8/PIRL/Strings/Makefile0000644000175000017500000000064010113732522016177 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.9 2004/08/27 22:27:30 castalia Exp # gmake syntax JPATH ?= ../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = String_Buffer.class \ String_Buffer_Reader.class \ Words.class \ String_Utilities.class \ strip_DEBUG.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Strings/String_Utilities.java0000644000175000017500000001407611742734407020730 0ustar mathieumathieu/* String_Utilities PIRL CVS ID: String_Utilities.java,v 1.7 2012/04/16 06:15:35 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Strings; /** The String_Utilities are a collection of static functions that operate on String objects to provide various commonly used operations.

@author Bradford Castalia, UA/PIRL @version 1.7 */ public class String_Utilities { /** Class identification with revision number and date. */ public static final String ID = "PIRL.Strings.String_Utilities (1.7 2012/04/16 06:15:35)"; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_UTILITIES = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; private String_Utilities () {} /** Trims all delimiter characters from both ends of a string.

@param string The String to trim. @param delimiter The character to be trimmed off the ends. @return The string, sans leading and trailing characers. */ public static String Trim ( String string, char delimiter ) { if (string == null) return string; int end; for (end = string.length () - 1; end >= 0; end--) if (string.charAt (end) != delimiter) break; end++; int start; for (start = 0; start < end; start++) if (string.charAt (start) != delimiter) break; return string.substring (start, end); } /** Gets that portion of a String preceeding a delimiter.

@param string The String from which to get the leading portion. @param delimiter The delimiter character. @return The portion of the String before the delimiter, or null if the delimiter is not present. */ public static String Preceeding ( String string, char delimiter ) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Preceeding: '" + delimiter + "' in " + string); if (string != null) { int index; if ((index = string.indexOf (delimiter)) >= 0) { string = string.substring (0, index); if (string.length () == 0) string = null; } else string = null; } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" preceeding: " + string); return string; } /** The default number of decimal digits used by the {@link #Amount_Magnitude(long, int)} function. */ public static final int DEFAULT_DECIMAL_DIGITS = 2, MAX_DECIMAL_DIGITS = 12; private static final char MAGNITUDE[] = {' ', 'K', 'M', 'G', 'T', 'P'}; /** Gets that portion of a String following a delimiter.

@param string The String from which to get the trailing portion. @param delimiter The delimiter character. @return The portion of the String following the delimiter, or null if the delimiter is not present. */ public static String Following ( String string, char delimiter ) { if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (">>> Following: '" + delimiter + "' in " + string); if (string != null) { int index; if ((index = string.indexOf (delimiter)) >= 0) { string = string.substring (++index); if (string.length () == 0) string = null; } else string = null; } if ((DEBUG & DEBUG_UTILITIES) != 0) System.out.println (" following: " + string); return string; } /** Provide a binary magnitude String representation of an amount.

An amount that is less than 1024 is returned as its String representation. Otherwise it is divided by 1024 until its value is less than 1024, up to a maximum of five times. The number of divisions determines the binary magnitude of the amount, the first character of which is then appended to the String representation of the reduced value:

  1. Kilo
  2. Mega
  3. Giga
  4. Tera
  5. Peta

Negative values are treated as positive values with the negative sign applied to the final result.

@param amount The amount to be represented. @param decimal_digits The number of decimal digits in the reduced amount representation. If this is negative the {@link #DEFAULT_DECIMAL_DIGITS} will be used. The number of digits will be limited to a maximum of {@link #MAX_DECIMAL_DIGITS}. @return A String representation of the amount with a binary magnitude suffix character if the amount was not less than 1024. */ public static String Amount_Magnitude ( long amount, int decimal_digits ) { if (amount < 1024 && amount > -1024) return String.valueOf (amount); if (decimal_digits < 0) decimal_digits = DEFAULT_DECIMAL_DIGITS; else if (decimal_digits > MAX_DECIMAL_DIGITS) decimal_digits = MAX_DECIMAL_DIGITS; String format = "%." + decimal_digits + 'f'; int magnitude; long number = amount; if (number < 0) number = -number; for (magnitude = 0, decimal_digits = 1; magnitude < MAGNITUDE.length && number >= 1024; magnitude++, number >>>= 10, decimal_digits *= 1024); return String.format (format, (double)amount / decimal_digits) + MAGNITUDE[magnitude]; } /** Provide a binary magnitude String representation of an amount.

The {@link #DEFAULT_DECIMAL_DIGITS} number of decimal digits in the reduced amount representation will be used.

@param amount The amount to be represented. @return A String representation of the amount with a binary magnitude suffix character if the amount was not less than 1024. @see #Amount_Magnitude(long, int) */ public static String Amount_Magnitude ( long amount ) {return Amount_Magnitude (amount, -1);} } pirl-2.3.8/PIRL/Strings/tests/0000755000175000017500000000000012052546515015712 5ustar mathieumathieupirl-2.3.8/PIRL/Strings/tests/Makefile0000644000175000017500000000051710116716274017355 0ustar mathieumathieu# Makefile for Java classes # CVS ID: Makefile,v 1.3 2004/09/05 23:09:48 castalia Exp JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< TESTS = String_Buffer_test.class \ Words_test.class \ String_Utilities_test.class all: ${TESTS} clean: rm -f *.class pirl-2.3.8/PIRL/Strings/tests/String_Utilities_test.java0000644000175000017500000000716211742734410023121 0ustar mathieumathieu/* String_Utilities_test CVS ID: String_Utilities_test.java,v 1.5 2012/04/16 06:15:36 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Strings.String_Utilities; import PIRL.Utilities.Checker; public class String_Utilities_test { public static void main (String[] arguments) { System.out.println ("*** String_Utilities_test:\n" + String_Utilities.ID); String string, expected; Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; string = ""; expected = string; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); string = "Word"; expected = string; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); string = " Word"; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); string = "Word "; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); string = " Word "; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); string = " Word "; checker.Check ("Trim (\"" + string + "\", ' ')", expected, String_Utilities.Trim (string, ' ')); long number = 0; int digits = 2; checker.Check ("Amount_Magnitude (" + number + ")", "0", String_Utilities.Amount_Magnitude (number)); number = 1024; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "1.00K", String_Utilities.Amount_Magnitude (number, digits)); number = -1024; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "-1.00K", String_Utilities.Amount_Magnitude (number, digits)); number = 1024; digits = 0; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "1K", String_Utilities.Amount_Magnitude (number, digits)); number = 1536; digits = 2; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "1.50K", String_Utilities.Amount_Magnitude (number, digits)); digits = 0; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "2K", String_Utilities.Amount_Magnitude (number, digits)); number = 2 * 1024 * 1024; digits = -1; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "2.00M", String_Utilities.Amount_Magnitude (number, digits)); number = (long)(3 * 1024 + 768) * 1024 * 1024; digits = 3; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "3.750G", String_Utilities.Amount_Magnitude (number, digits)); digits = 1; checker.Check ("Amount_Magnitude (" + number + ", " + digits + ")", "3.8G", String_Utilities.Amount_Magnitude (number, digits)); System.out.println ("Checks: " + checker.Checks_Total + '\n' +"Passed: " + checker.Checks_Passed); System.exit (0); } } pirl-2.3.8/PIRL/Strings/tests/Words_test.java0000644000175000017500000002067311742734410020720 0ustar mathieumathieu/* Words_test CVS ID: Words_test.java,v 1.13 2012/04/16 06:15:36 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Strings.Words; import PIRL.Utilities.Checker; import java.util.Vector; public class Words_test { public static void main (String[] arguments) { System.out.println ("*** Words_test: " + Words.ID); Checker checker = new Checker (); String string; if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; string = " First\tSecond\n\tThird=third&Fourth=fourth"; if (checker.Verbose) System.out.println ("--- words = new Words (String)\n" +" String -\n>|" + string + "|<"); Words words = new Words (string); checker.Check ("new Words (\"" + string + "\"); toString ()", string, words.toString ()); checker.Check ("First words.Next_Word ()", "First", words.Next_Word ()); checker.Check ("Second words.Next_Word ()", "Second", words.Next_Word ()); checker.Check ("Third words.Next_Word ()", "Third=third&Fourth=fourth", words.Next_Word ()); checker.Check ("Fourth words.Next_Word ()", "", words.Next_Word ()); words.Location (3); checker.Check ("words.Location (3), Start_Index", 3, words.Start_Index); checker.Check ("words.Location (3), End_Index", 3, words.End_Index); words.Restore (); checker.Check ("words.Restore (), Start_Index", 0, words.Start_Index); checker.Check ("words.Restore (), End_Index", 0, words.End_Index); words.Delimiters (Words.DEFAULT_DELIMITERS + "=&"); checker.Check ("words.Delimiters (Words.DEFAULT_DELIMITERS + \"=&\")", Words.DEFAULT_DELIMITERS + "=&", words.Delimiters ()); words.Mask ("XXX"); checker.Check ("words.Mask (\"XXX\")", "XXX", words.Mask ()); Vector names = new Vector (), expected_names = new Vector (); names.add ("Third"); names.add ("Fourth"); words.Mask (names); checker.Check ("words.Mask (Vector " + names + ")", " First\tSecond\n\tThird=XXX&Fourth=XXX", words.toString ()); names = words.Restore ().Split (); expected_names.clear (); expected_names.add ("First"); expected_names.add ("Second"); expected_names.add ("Third"); expected_names.add ("XXX"); expected_names.add ("Fourth"); expected_names.add ("XXX"); checker.Check ("words.Restore ().Split ()", expected_names, names); names = words.Characters ("First/1 second ").Delimiters ("/ ").Split (2); expected_names.clear (); expected_names.add ("First"); expected_names.add ("1 second "); checker.Check ("words.Characters (\"First/1 second \").Delimiters (\"/ \").Split (2)", expected_names, names); names = words.Restore ().Split (3); expected_names.clear (); expected_names.add ("First"); expected_names.add ("1"); expected_names.add ("second "); checker.Check ("words.Restore ().Split (3)", expected_names, names); names = words.Restore ().Split (-3); expected_names.clear (); expected_names.add ("First"); expected_names.add ("1"); expected_names.add (" second "); checker.Check ("words.Restore ().Split (-3)", expected_names, names); names = words.Restore ().Split (); expected_names.clear (); expected_names.add ("First"); expected_names.add ("1"); expected_names.add ("second"); checker.Check ("words.Restore ().Split ()", expected_names, names); // Quoted words. string = "con\"joined\""; names = words.Characters (string).Delimit_at_Quote (false).Split (); expected_names.clear (); expected_names.add ("con\"joined\""); checker.Check ("words.Characters (\"" + string + "\").Delimit_at_Quote (false).Split ()", expected_names, names); string = "A \"quoted \\\"word\\\" string\" line'test'"; words.Characters (string).Delimit_at_Quote (true); checker.Check ("words.Characters (\"" + string + "\").Delimit_at_Quote (true)", string, words.toString ()); checker.Check ("First unquoted words.Next_Word ()", "A", words.Next_Word ()); checker.Check ("Second quoted words.Next_Word ()", "\"quoted \\\"word\\\" string\"", words.Next_Word ()); checker.Check ("Third unquoted words.Next_Word () preceeding contiguous quote", "line", words.Next_Word ()); checker.Check ("Contiguous quoted fourth words.Next_Word ()", "'test'", words.Next_Word ()); words.Quoted_Words (false); checker.Check ("words.Quoted_Words (false)", false, words.Quoted_Words ()); checker.Check ("words.Restore ().toString ()", string, words.Restore ().toString ()); checker.Check ("Ignore quotes first words.Next_Word ()", "A", words.Next_Word ()); checker.Check ("Ignore quotes second words.Next_Word ()", "\"quoted", words.Next_Word ()); checker.Check ("Ignore quotes third words.Next_Word ()", "\\\"word\\\"", words.Next_Word ()); checker.Check ("Ignore quotes fourth words.Next_Word ()", "string\"", words.Next_Word ()); checker.Check ("Ignore quotes fifth words.Next_Word ()", "line'test'", words.Next_Word ()); string = "(A)','B','(C)"; if (checker.Verbose) System.out.println ("--- words.Characters (\"" + string + "\").Quoted_Words (true).Parenthesized_Words (true)"); words.Characters (string).Quoted_Words (true).Parenthesized_Words (true); checker.Check ("Combined paren/quote/clear/quote/paren first words.Next_Word ()", "(A)", words.Next_Word ()); checker.Check ("Combined paren/quote/clear/quote/paren second words.Next_Word ()", "','", words.Next_Word ()); checker.Check ("Combined paren/quote/clear/quote/paren third words.Next_Word ()", "B", words.Next_Word ()); checker.Check ("Combined paren/quote/clear/quote/paren fourth words.Next_Word ()", "','", words.Next_Word ()); checker.Check ("Combined paren/quote/clear/quote/paren fifth words.Next_Word ()", "(C)", words.Next_Word ()); // Parenthesized words. string = "The (parens) (are (nested)) word(characters)"; words.Characters (string).Parenthesized_Words (false); checker.Check ("words.Characters (\"" + string + "\")", string, words.toString ()); checker.Check ("Ignore parenthesizes first words.Next_Word ()", "The", words.Next_Word ()); checker.Check ("Ignore parenthesizes second words.Next_Word ()", "(parens)", words.Next_Word ()); checker.Check ("Ignore parenthesizes third words.Next_Word ()", "(are", words.Next_Word ()); checker.Check ("Ignore parenthesizes fourth words.Next_Word ()", "(nested))", words.Next_Word ()); checker.Check ("Ignore parenthesizes fifth words.Next_Word ()", "word(characters)", words.Next_Word ()); words.Parenthesized_Words (true).Restore (); checker.Check ("words.Parenthesized_Words (true)", true, words.Parenthesized_Words ()); checker.Check ("words.Restore ().toString ()", string, words.Restore ().toString ()); checker.Check ("First unparenthesized words.Next_Word ()", "The", words.Next_Word ()); checker.Check ("Second parenthesized words.Next_Word ()", "(parens)", words.Next_Word ()); checker.Check ("Third parenthesized with nested parentheses words.Next_Word ()", "(are (nested))", words.Next_Word ()); checker.Check ("Fourth unparenthesized words.Next_Word () preceeding contiguous paren", "word", words.Next_Word ()); if (checker.Verbose) System.out.println ("--- words.Mark ()"); words.Mark (); checker.Check ("words.Mark_Index.Start_Index", 28, words.Mark_Index.Start_Index); checker.Check ("words.Mark_Index.End_Index", 32, words.Mark_Index.End_Index); checker.Check ("Contiguous parenthesized fifth words.Next_Word ()", "(characters)", words.Next_Word ()); words.Restore (); checker.Check ("words.Restore (); words.Mark_Index.Start_Index", 28, words.Mark_Index.Start_Index); checker.Check ("words.Mark_Index.End_Index", 32, words.Mark_Index.End_Index); checker.Check ("After Restore contiguous parenthesized fifth words.Next_Word ()", "(characters)", words.Next_Word ()); System.out.println ("Checks: " + checker.Checks_Total + '\n' +"Passed: " + checker.Checks_Passed); System.exit (checker.Checks_Total - checker.Checks_Passed); } } pirl-2.3.8/PIRL/Strings/tests/String_Buffer_test.java0000644000175000017500000001344511742734410022360 0ustar mathieumathieu/* String_Buffer_test CVS ID: String_Buffer_test.java,v 1.7 2012/04/16 06:15:36 castalia Exp Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Strings.String_Buffer; import PIRL.Utilities.Checker; public class String_Buffer_test { public static void main (String[] arguments) { System.out.println ("*** String_Buffer_test:\n" + String_Buffer.ID); Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; String string = "+ZERO.X---=--==-===+SECOND.Y+-THIRTY!", indexing = " 111111111122222222223333333\n" + "0123456789012345678901234567890123456\n", delimiters = "+=-"; if (checker.Verbose) System.out.println ("Test String -\n" + string + '\n' + indexing); if (checker.Verbose) System.out.println ("--- Constructor"); String_Buffer SB = new String_Buffer (string); if (! checker.Check ("length", 37, SB.length ()) || ! checker.Check ("toString ()", string, SB.toString ())) System.exit (1); int index = 0, end; checker.Check ("index_of (0, \"negative one\")", -1, SB.index_of (0, "negative one")); checker.Check ("index_of (0, \"THIRTY\")", 30, (index = SB.index_of (0, "THIRTY"))); checker.Check ("equals_ignore_case (" + index + ", \"Thirty\")", true, SB.equals_ignore_case (index, "Thirty")); checker.Check ("substring (6, 7)", "X", SB.substring (6, 7)); checker.Check ("skip_until (6, \"+\")", 19, (index = SB.skip_until (6, "+"))); checker.Check ("skip_back_over (" + index + ", \"" + delimiters + "\")", 6, SB.skip_back_over (index, delimiters)); index = 0; checker.Check ("skip_over (" + index + ", \"" + delimiters + "\")", 1, (index = SB.skip_over (index, delimiters))); checker.Check ("skip_until (" + index + "\".\")", 5, (end = SB.skip_until (index, "."))); if (end < 0) end = SB.length (); checker.Check ("substring (" + index + ", " + end + ")", "ZERO", SB.substring (index, end)); checker.Check ("skip_until (" + end + ", \"" + delimiters + "\")", 7, (index = SB.skip_until (end, delimiters))); checker.Check ("skip_over (" + index + ", \"" + delimiters + "\")", 20, (index = SB.skip_over (index, delimiters))); checker.Check ("skip_until (" + index + "\".\")", 26, (end = SB.skip_until (index, "."))); if (end < 0) end = SB.length (); checker.Check ("substring (" + index + ", " + end + ")", "SECOND", SB.substring (index, end)); checker.Check ("skip_until (" + end + ", \"" + delimiters + "\")", 28, (index = SB.skip_until (end, delimiters))); checker.Check ("skip_over (" + index + ", \"" + delimiters + "\")", 30, (index = SB.skip_over (index, delimiters))); checker.Check ("skip_until (" + index + "\".\")", -1, (end = SB.skip_until (index, "."))); if (end < 0) end = SB.length (); checker.Check ("substring (" + index + ", " + end + ")", "THIRTY!", SB.substring (index, end)); checker.Check ("replace (0, \".\", \"_._\")", "+ZERO_._X---=--==-===+SECOND_._Y+-THIRTY!", SB.replace (0, ".", "_._").toString ()); checker.Check ("replace_span (0, \"" + delimiters + "\", \"<->\")", "<->ZERO_._X<->SECOND_._Y<->THIRTY!", SB.replace_span (0, delimiters, "<->").toString ()); string = "... HT>|\t||\n||\07||\r|<"; String expected = "... HT>|\\t||\\n||\\07||\\r|<"; if (checker.Verbose) System.out.println ("\nTest String -\n" + string); SB = new String_Buffer (string); checker.Check ("special_to_escape ()", expected, SB.special_to_escape ().toString ()); checker.Check ("special_to_escape (String)", expected, String_Buffer.special_to_escape (string)); checker.Check ("escape_to_special ()", string, SB.escape_to_special ().toString ()); checker.Check ("escape_to_special (String)", string, String_Buffer.escape_to_special (expected)); expected = "... HT>| || |||| |<"; SB = new String_Buffer (string); checker.Check ("to_character_references ()", expected, SB.to_character_references ().toString ()); checker.Check ("to_character_references (String)", expected, String_Buffer.to_character_references (string)); string = "FooBar"; expected = "FooBar"; if (checker.Verbose) System.out.println ("\nTest String: " + string); SB = new String_Buffer (string); checker.Check ("from_character_references ()", expected, SB.from_character_references ().toString ()); checker.Check ("from_character_references (String)", expected, String_Buffer.from_character_references (string)); string = "... HT>|\t||\n||\07||\r|<"; if (checker.Verbose) System.out.println ("\nTest String -\n" + string); expected = "... HT>|?||?||?||?|<"; SB = new String_Buffer (string); checker.Check ("to_printable_ASCII ()", expected, SB.to_printable_ASCII ().toString ()); checker.Check ("to_printable_ASCII (String)", expected, String_Buffer.to_printable_ASCII (string)); System.out.println (String_Buffer.NL +"Checks: " + checker.Checks_Total + String_Buffer.NL +"Passed: " + checker.Checks_Passed); System.exit (checker.Checks_Total - checker.Checks_Passed); } } pirl-2.3.8/PIRL/Strings/package.html0000644000175000017500000000257311742734410017036 0ustar mathieumathieu The PIRL Strings package provides character string manipulation capabilities. The String_Buffer class provides a wrapper around the basic, and final, StringBuffer class so the latter can be extended. It also contains a collection of convenient search and replace methods. The String_Buffer_Reader is an extension that provides a virtual String_Buffer backed by the contents of a reader. pirl-2.3.8/PIRL/Utilities/0000755000175000017500000000000012052546514015071 5ustar mathieumathieupirl-2.3.8/PIRL/Utilities/Checker.java0000644000175000017500000002074511742734660017316 0ustar mathieumathieu/* Checker PIRL CVS ID: Checker.java,v 1.6 2012/04/16 06:18:24 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; /** A Checker is used to compare expected against obtained results for unit testing.

A set of Check methods compares an expected argument value with an obtained argument value for equality. When the arguments match the word "PASS" is printed, otherwise the word "FAIL" is printed. This is followed by ": " and the description String that very briefly identifies what was checked (i.e. the unit test that occured). The obtained value will then be printed following " - " if the Show_Obtained flag is enabled. If the comparison does not match, or the Check is Verbose, the expected and obtained arguments are printed on the next two lines following an "====> expected: " and "====> obtained: " prefix.

Check methods are provided for integer, floating point, boolean and char values as well as String and generic Object values. String values are printed enclosed in double quotes (") and char values are listed enclosed in single quotes (').

The total number of checks performed (Checks_Total), and the number in which the comparison matched (Checks_Passed), is accumulated by each Check method.

@author Bradford Castalia @version 1.6 */ public class Checker { public static final String ID = "PIRL.Utilities.Checker (1.6 2012/04/16 06:18:24)"; /** Total number of Checks performed. */ public int Checks_Total = 0; /** Total number of Checks where expected matched obtained. */ public int Checks_Passed = 0; /** Set to true to always show both the expected and obtained values.

Default is false; */ public boolean Verbose = false; /** Set to true to always show the obtained value.

Default is false; */ public boolean Show_Obtained = false; /*============================================================================== Checks */ /** Checks integer values. @param description A brief description of what was checked. @param expected The expected value. @param obtained The obtained value. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, long expected, long obtained ) { boolean passed = obtained == expected; System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else if (Show_Obtained) System.out.println (" - " + obtained); else System.out.println (); return Check (passed); } /** Checks floating point values. @param description A brief description of what was checked. @param expected The expected value. @param obtained The obtained value. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, double expected, double obtained ) { boolean passed = obtained == expected; System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else if (Show_Obtained) System.out.println (" - " + obtained); else System.out.println (); return Check (passed); } /** Checks boolean values. @param description A brief description of what was checked. @param expected The expected value. @param obtained The obtained value. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, boolean expected, boolean obtained ) { boolean passed = obtained == expected; System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else if (Show_Obtained) System.out.println (" - " + obtained); else System.out.println (); return Check (passed); } /** Checks char values. @param description A brief description of what was checked. @param expected The expected value. @param obtained The obtained value. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, char expected, char obtained ) { boolean passed = obtained == expected; System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: '" + expected + "'\n" +"====> obtained: '" + obtained + "'"); else if (Show_Obtained) System.out.println (" - '" + obtained + "'"); else System.out.println (); return Check (passed); } /** Checks String values. @param description A brief description of what was checked. @param expected The expected value. @param obtained The obtained value. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, String expected, String obtained ) { boolean passed = (expected == null && obtained == null) || (expected != null && expected.equals (obtained)); System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: \"" + expected + "\"\n" +"====> obtained: \"" + obtained + '"'); else if (Show_Obtained) System.out.println (" - \"" + obtained + '"'); else System.out.println (); return Check (passed); } /** Checks Exceptions.

If both the excpected and obtained exception are null they are equal. If both are non-null the {@link Object#getClass() class} of each are checked for equality. If only one exception is null they are not equal.

@param description A brief description of what was checked. @param expected The expected Exception. @param obtained The obtained Exception. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, Exception expected, Exception obtained ) { boolean passed = (expected == null && obtained == null) || (expected != null && obtained != null && expected.getClass ().equals (obtained.getClass ())); System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else if (Show_Obtained) System.out.println (" - " + obtained); else System.out.println (); return Check (passed); } /** Checks Objects.

If both objects are null they are equal. N.B.: The {@link Object#equals(Object)} method is used to compare a non-null expected Object with the obtained object, which may not be what is expected since this comparison is only true if both arguments are the same Object reference.

@param description A brief description of what was checked. @param expected The expected Object. @param obtained The obtained Object. @return true if expected equals obtained; false otherwise. */ public boolean Check ( String description, Object expected, Object obtained ) { boolean passed = (expected == null && obtained == null) || (expected != null && expected.equals (obtained)); System.out.print ((passed ? "PASS" : "FAIL") + ": " + description); if (Verbose || ! passed) System.out.println ('\n' +"====> expected: " + expected + '\n' +"====> obtained: " + obtained); else if (Show_Obtained) System.out.println (" - " + obtained); else System.out.println (); return Check (passed); } /** Updates the check accounting.

The Checks_Total accumulator is incremented. The Checks_Passed accumulator is incremented if passed is true.

@param passed true if the external test passed; false otherwise. @return The passed value. */ public boolean Check ( boolean passed ) { Checks_Total++; if (passed) Checks_Passed++; return passed; } } pirl-2.3.8/PIRL/Utilities/Styled_Multiwriter.java0000644000175000017500000005642711742734660021633 0ustar mathieumathieu/* Styled_Multiwriter PIRL CVS ID: Styled_Multiwriter.java,v 1.12 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.io.Writer; import java.io.IOException; import java.util.Vector; import javax.swing.text.AttributeSet; /** A Styled_Multiwriter is a one-to-many Writer, any of which may be a Styled_Writer.

Writers are wrapped in a Suspendable_Styled_Writer before being added to the list of Writers unless they are already a Suspendable_Styled_Writer.

Each write method is repeated on each Writer in the Writers list. Each Writer is used even if a writer throws an exception. Any exceptions thrown are accumulated in a Multiwriter_IOException which associates the exception thrown with the original Writer source of the exception. A Writer that throws an exception is suspended, except in the case of the close method.

N.B.: If the Writers list is empty each write method will do nothing.

@author Bradford Castalia - UA/PIRL @version 1.12 @see Writer @see Styled_Writer @see Suspendable_Styled_Writer */ public class Styled_Multiwriter extends Writer implements Styled_Writer { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utilities.Styled_Multiwriter (1.12 2012/04/16 06:18:24)"; /** Determines whether a Writer is enabled or disabled when it is {@link #Add(Writer) add}ed to the Writers list. */ public volatile boolean Add_Enabled = true; // List of Suspendable_Styled_Writer objects. private Vector Writers = new Vector (); private Vector Unwrapped = new Vector (); private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_WRITE = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Styled_Multiwriter.

Writers have yet to be {@link #Add(Writer) add}ed. */ public Styled_Multiwriter () { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Styled_Multiwriter"); } /*============================================================================== Accessors */ /** Add a Writer to the list of {@link #Writers() Writers}.

If the Writer is already in the list it is not added. A Writer that is added to the list is wrapped in a Suspendable_Styled_Writer unless it is already an instance of a Suspendable_Styled_Writer.

The addition of the Writer to the Writers list is synchronized on the Writers list.

@param writer The Writer to add. @return true if the Writer was added to the list; false if it was already in the list or is null. @see Suspendable_Styled_Writer @see #Remove(Writer) */ public boolean Add ( Writer writer ) { if (writer == null || Contains (writer)) return false; if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> Styled_Multiwriter.Add: " + writer); if (writer instanceof Suspendable_Styled_Writer) Unwrapped.add (writer); else writer = new Suspendable_Styled_Writer (writer, Add_Enabled); synchronized (Writers) {Writers.add (writer);} if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< Styled_Multiwriter.Add: true"); return true; } /** Remove a Writer from the list of {@link #Writers() Writers}.

The removal of the Writer from the Writers list is synchronized on the Writers list.

@param writer The Writer to remove. This is expected to be a Writer that was {@link #Add(Writer) Add}ed. @return true if the Writer was removed to the list; false if it was not in the list or is null. @see #Add(Writer) @see #Entry_for(Writer) */ public boolean Remove ( Writer writer ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Styled_Multiwriter.Remove: " + writer); if ((writer = Entry_for (writer)) == null) return false; synchronized (Writers) { Writers.remove (writer); Unwrapped.remove (writer); } return true; } /** Remove all {@link #Writers() Writers} list entries.

The removal of the Writer from the Writers list is synchronized on the Writers list.

N.B.: The Writers entries are not {@link #close() closed}.

@see #Add(Writer) */ public void Remove_All () { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Styled_Multiwriter.Remove_All"); synchronized (Writers) {Writers.clear ();} } /** Turn output suspension on or off for a Writer.

Searching the Writers list for the Writer is synchronized on the Writers list,

@param writer The Writer to be affected. This is expected to be a Writer that was {@link #Add(Writer) Add}ed. @param suspend true if the Writer output is to be suspended; false if output is to be allowed. @return true If the Writer was found in the Writers list; false otherwise. @see #Suspended(Writer) @see #Entry_for(Writer) */ public boolean Suspend ( Writer writer, boolean suspend ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Styled_Multiwriter.Suspend: " + writer + ' ' + suspend); Suspendable_Styled_Writer suspendable_writer = Entry_for (writer); if (suspendable_writer != null) { suspendable_writer.Suspend (suspend); return true; } return false; } /** Test if output for a Writer is suspended.

Searching the Writers list for the Writer is synchronized on the Writers list,

@param writer The Writer to test. This is expected to be a Writer that was {@link #Add(Writer) Add}ed. @return true if the Writer output is suspended or could not be be found in the Writers list; false if the Writer is not suspended. N.B.: Because a Writer that is not in the Writers list will be deemed suspended - it will not produce any output via this object - test if the Writer is {@link #Contains(Writer) contained} in the list if the distinction is important. @see #Suspend(Writer, boolean) */ public boolean Suspended ( Writer writer ) { Suspendable_Styled_Writer suspendable_writer = Entry_for (writer); if (suspendable_writer != null) return suspendable_writer.Suspended (); return true; } /** Test if the list of Writers contains a specified Writer.

Searching the Writers list for the Writer is synchronized on the Writers list,

@param writer The Writer to test for. This is expected to be a Writer that was {@link #Add(Writer) Add}ed. @return true If the Writer is in the Writers list; false otherwise. @see #Entry_for(Writer) */ public boolean Contains ( Writer writer ) {return Entry_for (writer) != null;} /** Get the {@link #Writers() Writers} entry for a Writer that was {@link #Add(Writer) Add}ed.

When a Writer is {@link #Add(Writer) Add}ed it is wrapped in a Suspendable_Styled_Writer if it is not already an instance of this class. This method searches for the Suspendable_Styled_Writer in the {@link #Writers() Writers} list that is associated with a specified Writer which may or may not have been wrapped by a Suspendable_Styled_Writer before being entered into the list.

Searching the Writers list for the Writer is synchronized on the Writers list,

@param writer A Writer. This is expected to be one of the Writer objects that was {@link #Add(Writer) Add}ed. @return A Suspendable_Styled_Writer from the {@link #Writers() Writers} list. This will be null if the writer was not found. */ public Suspendable_Styled_Writer Entry_for ( Writer writer ) { if (writer == null) return null; synchronized (Writers) { int index = Writers.size (); while (--index >= 0) { Suspendable_Styled_Writer suspendable_writer = (Suspendable_Styled_Writer)Writers.get (index); if (writer == suspendable_writer || writer == suspendable_writer.Writer ()) return suspendable_writer; } } return null; } /** Get the Writer that was {@link #Add(Writer) Add}ed from a {@link #Writers() Writers} list entry.

When a Writer is {@link #Add(Writer) Add}ed it is wrapped in a Suspendable_Styled_Writer if it is not already an instance of this class. This method searches for the Suspendable_Styled_Writer in the {@link #Writers() Writers} list that is associated with a specified Writer which may or may not have been wrapped by a Suspendable_Styled_Writer before being entered into the list. It then checks this against the list of Suspendable_Styled_Writer objects that were added without being wrapped. If this check succeeds then the Writer that was specified is returned; otherwise the {@link Suspendable_Styled_Writer#Writer() Writer} from this specified Suspendable_Styled_Writer is returned.

Searching the Writers list is synchronized on the Writers list,

@param writer A Suspendable_Styled_Writer from which to determine the original Writer that was {@link #Add(Writer) Add}ed. @return The Writer that was {@link #Add(Writer) Add}ed, or null if the writer is not in the {@link #Writers() Writers} list. */ public Writer Entry_from ( Suspendable_Styled_Writer writer ) { if (writer == null) return null; synchronized (Writers) { if (! Writers.contains (writer)) return null; if (Unwrapped.contains (writer)) return writer; return writer.Writer (); } } /** Test if the Writers list is empty.

@return true if the Writers list is empty; false otherwise. */ public boolean Is_Empty () {return Writers.isEmpty ();} /** Get a reference to the list of Writers.

The Writers in the list are {@link Suspendable_Styled_Writer} objects. When a Writer is {@link #Add(Writer) Add}ed it is wrapped in a Suspendable_Styled_Writer if it is not already an instance of this class. A Suspendable_Styled_Writer always {@link Suspendable_Styled_Writer#Writer() contains a Writer} to which I/O is deferred.

@return A Vector of Suspendable_Styled_Writer objects. N.B.: The returned list is a reference, not a copy. DO NOT MODIFY THE CONTENTS OF THIS LIST! @see #Add(Writer) @see #Remove(Writer) */ public Vector Writers () {return Writers;} /*============================================================================== Writer */ /** Write a portion of an array of characters.

Iterating over the Writers list is synchronized on the Writers list, and the write to the Writer is synchronized on the Writer.

All Writers are written even if one or more Writers throws an exception.

@param characters The char array containing the characters to be written. If null or empty nothing is done. @param offset Array offset from which to start writing characters. @param amount The number of characters to write. If zero nothing is done. @throws IndexOutOfBoundsException If the offset or amount are negative or the offset plus the amount is greater than the length of the array. @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. @see #Suspended(Writer) */ public void write ( char[] characters, int offset, int amount ) throws Multiwriter_IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Styled_Multiwriter.write: " + offset + '/' + amount + " -" + NL + characters); if (characters == null || characters.length == 0 || amount == 0) return; if (offset < 0 || amount < 0 || (offset + amount) > characters.length) throw new IndexOutOfBoundsException (ID + NL + "Invalid values for " + characters.length + " character array content -" + NL + " Array offset " + offset + " and amount " + amount + '.'); Multiwriter_IOException multiexception = null; synchronized (Writers) { // Write in the order of Writers entries. int index = -1, size = Writers.size (); while (++index < size) { Suspendable_Styled_Writer writer = (Suspendable_Styled_Writer)Writers.get (index); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Styled_Multiwriter: Writer " + writer.Writer ()); try {synchronized (writer.Writer ()) {writer.write (characters, offset, amount);}} catch (IOException exception) { if (multiexception == null) multiexception = new Multiwriter_IOException (ID + NL + "Problem encountered while writing characters -" + NL + new String (characters)); multiexception.Sources.add (Entry_from (writer)); multiexception.Exceptions.add (exception); writer.Suspend (true); } } } if (multiexception != null) throw multiexception; if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Styled_Multiwriter.write"); } /** Write an array of characters.

Using this method is the same as using {@link #write(char[], int, int) write (characters, 0, characters.length)}.

All Writers are written even if one or more Writers throws an exception.

@param characters The char array containing the characters to be written. If null or empty nothing is done. @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. */ public void write ( char[] characters ) throws Multiwriter_IOException { if (characters == null) return; write (characters, 0, characters.length); } /** Write a character.

Iterating over the Writers list is synchronized on the Writers list, and the write to the Writer is synchronized on the Writer.

All Writers are written even if one or more Writers throws an exception.

@param character The character to be written in the 16 low-order bits.. @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. */ public void write ( int character ) throws Multiwriter_IOException { Multiwriter_IOException multiexception = null; synchronized (Writers) { // Write in the order of Writers entries. int index = -1, size = Writers.size (); while (++index < size) { Suspendable_Styled_Writer writer = (Suspendable_Styled_Writer)Writers.get (index); try {synchronized (writer.Writer ()) {writer.write (character);}} catch (IOException exception) { if (multiexception == null) multiexception = new Multiwriter_IOException (ID + NL + "Problem encountered while writing character '" + (char)character + "'."); multiexception.Sources.add (Entry_from (writer)); multiexception.Exceptions.add (exception); writer.Suspend (true); } } } if (multiexception != null) throw multiexception; } /** Write a portion of a String.

Using this method is the same as using {@link #write(char[], int, int) write (string.toCharArray (), offset, amount)}.

All Writers are written even if one or more Writers throws an exception.

@param string The String containing the characters to be written. If null or empty nothing is done. @param offset String offset from which to start writing characters. @param amount The number of characters to write. If zero nothing is done. @throws IndexOutOfBoundsException If the offset or amount are negative or the offset plus the amount is greater than the length of the string. @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. @see #write(char[], int, int) */ public void write ( String string, int offset, int amount ) throws Multiwriter_IOException { if (string == null || string.length () == 0 || amount == 0) return; if (offset < 0 || amount < 0 || (offset + amount) > string.length ()) throw new IndexOutOfBoundsException (ID + NL + "Invalid values for " + string.length () + " character String content -" + NL + " String offset " + offset + " and amount " + amount + '.'); write (string.toCharArray (), offset, amount); } /** Write a String.

Using this method is the same as using {@link #write(String, int, int) write (string, 0, string.length ())}.

All Writers are written even if one or more Writers throws an exception.

@param string The String containing the characters to be written. If null or empty nothing is done. @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. @see #write(String, int, int) */ public void write ( String string ) throws Multiwriter_IOException { if (string == null) return; write (string, 0, string.length ()); } /** Flush the writers.

Iterating over the Writers list is synchronized on the Writers list, and the flush to the Writer is synchronized on the Writer.

All Writers are flushed even if one or more Writers throws an exception.

@throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. */ public void flush () throws Multiwriter_IOException { Multiwriter_IOException multiexception = null; synchronized (Writers) { // Write in the order of Writers entries. int index = -1, size = Writers.size (); while (++index < size) { Suspendable_Styled_Writer writer = (Suspendable_Styled_Writer)Writers.get (index); try {synchronized (writer.Writer ()) {writer.flush ();}} catch (IOException exception) { if (multiexception == null) multiexception = new Multiwriter_IOException (ID + NL + "Problem encountered while flushing."); multiexception.Sources.add (Entry_from (writer)); multiexception.Exceptions.add (exception); writer.Suspend (true); } } } if (multiexception != null) throw multiexception; } /** Close the writers.

Iterating over the Writers list is synchronized on the Writers list, and the close of the Writer is synchronized on the Writer.

@throws Multiwriter_IOException If any Writer throws an exception. All Writers are closed even if one or more Writers throws an exception. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. N.B.: A Writer that throws an exception is not suspended. */ public void close () throws Multiwriter_IOException { Multiwriter_IOException multiexception = null; synchronized (Writers) { int index = Writers.size (); while (--index >= 0) { Suspendable_Styled_Writer writer = (Suspendable_Styled_Writer)Writers.get (index); try {synchronized (writer.Writer ()) {writer.close ();}} catch (IOException exception) { if (multiexception == null) multiexception = new Multiwriter_IOException (ID + NL + "Problem encountered while closing."); multiexception.Sources.add (Entry_from (writer)); multiexception.Exceptions.add (exception); } } } if (multiexception != null) throw multiexception; } /*============================================================================== Styled_Writer */ /** Write styled text.

Iterating over the Writers list is synchronized on the Writers list, and the Write to the Writer is synchronized on the Writer.

All Writers are written even if one or more Writers throws an exception.

@param text The text String to be written. If null or empty nothing is done. @param style The AttributeSet to be applied to the text. This may be null if plain text is to be displayed. @return This Styled_Multiwriter @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. @see Suspendable_Styled_Writer#Write(String, AttributeSet) @see Styled_Writer#Write(String, AttributeSet) */ public Styled_Writer Write ( String text, AttributeSet style ) throws Multiwriter_IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Styled_Multiwriter.Write:" + NL +" Style: " + style + NL + text); if (text == null || text.length () == 0) return this; Multiwriter_IOException multiexception = null; synchronized (Writers) { // Write in the order of Writers entries. int index = -1, size = Writers.size (); while (++index < size) { Suspendable_Styled_Writer writer = (Suspendable_Styled_Writer)Writers.get (index); if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (" Styled_Multiwriter: Writer " + writer.Writer ()); try {synchronized (writer.Writer ()) {writer.Write (text, style);}} catch (IOException exception) { if (multiexception == null) multiexception = new Multiwriter_IOException (ID + NL + "Problem encountered while Writing " + ((style == null) ? "" : "styled ") + "text -" + NL + text); multiexception.Sources.add (Entry_from (writer)); multiexception.Exceptions.add (exception); writer.Suspend (true); } } } if (multiexception != null) throw multiexception; if ((DEBUG & DEBUG_WRITE) != 0) System.out.println ("<<< Styled_Multiwriter.Write"); return this; } /** Write plain text.

This is the same as {@link #Write(String, AttributeSet) writing} text with a null style.

All Writers are written even if one or more Writers throws an exception.

@param text The text String to be written. @return This Styled_Multiwriter @throws Multiwriter_IOException If any Writer throws an exception. A Writer that throws an exception is {@link Suspendable_Styled_Writer#Suspend(boolean) suspend}ed. The Writers in the exception's {@link Multiwriter_IOException#Sources} list are the original Writers that were {@link #Add(Writer) Add}ed. */ public Styled_Writer Write ( String text ) throws Multiwriter_IOException {return Write (text, null);} } pirl-2.3.8/PIRL/Utilities/Host.java0000644000175000017500000001621711742734660016666 0ustar mathieumathieu/* Host PIRL CVS ID: Host.java,v 1.7 2012/04/16 06:18:24 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Properties; import java.io.IOException; /** The Host class provides information about the host on which the process is running.

@author Bradford Castalia, UA/PIRL @version 1.7 */ public class Host { public static final String ID = "PIRL.Utilities.Host (1.7 2012/04/16 06:18:24)"; /** The cannonical, fully qualified name of the host system.

If the hostname can not be obtained from the host system the {@link #DEFAULT_HOSTNAME} will be used.

@see #SHORT_HOSTNAME */ public static final String FULL_HOSTNAME; /** The short, single word name of the host system.

If the hostname can not be obtained from the host system the {@link #DEFAULT_HOSTNAME} will be used.

@see #FULL_HOSTNAME */ public static final String SHORT_HOSTNAME; /** The Internet Protocol address of the host system.

If the IP address can not be obtained from the host system the {@link #DEFAULT_HOSTNAME} will be used. */ public static final String IP_ADDRESS; /** The name that will be provided for the hostname or IP address if they can not be obtained from the host system. */ public static final String DEFAULT_HOSTNAME = "unknown"; static { String full_hostname = null, short_hostname = null, ip_address = null; try { InetAddress address = InetAddress.getLocalHost (); full_hostname = address.getCanonicalHostName (); short_hostname = address.getHostName (); ip_address = address.getHostAddress (); } catch (UnknownHostException exception) { if (full_hostname == null) full_hostname = DEFAULT_HOSTNAME; if (short_hostname == null) short_hostname = DEFAULT_HOSTNAME; if (ip_address == null) ip_address = DEFAULT_HOSTNAME; } FULL_HOSTNAME = full_hostname; SHORT_HOSTNAME = short_hostname; IP_ADDRESS = ip_address; } //! No object constructor, just static functions. private Host () {} /** Get the name of the host system.

@return The hostname String of the local system. If the hostname can not be determined, the {@link #DEFAULT_HOSTNAME} is returned. @see #SHORT_HOSTNAME */ public static String Hostname () {return SHORT_HOSTNAME;} /** Get the name of a host system.

@param address The textual representation of a host system IP address or its hostname. If null, the local host system is assumed. @return The hostname String for the address. This will be null if hostname lookup failed. N.B.: Unlike for the {@link #Hostname() local system}, the {@link #DEFAULT_HOSTNAME} will not be returned if hostname lookup fails; instead of an ambiguous name for possibly multiple unknown hostnames, the calling application is expected to have rules to deal with an unknown hostname in this case. @throws SecurityException If hostname lookup is not allowed. */ public static String Hostname ( String address ) { try {return InetAddress.getByName (address).getHostName ();} catch (UnknownHostException exception) {} return null; } /** Get the fully qualified name of the host system.

@return The fully qualified hostname String of the local system. If the hostname can not be determined, the {@link #DEFAULT_HOSTNAME} is returned. @see #FULL_HOSTNAME */ public static String Full_Hostname () {return FULL_HOSTNAME;} /** Get the fully qualified name for a hostname.

@param hostname The name host system or the textual representation of its IP address. If null, the local host system is assumed. @return The fully qualified hostname String for the hostname. This will be null if hostname lookup failed. N.B.: Unlike for the {@link #Full_Hostname() local system}, the {@link #DEFAULT_HOSTNAME} will not be returned if hostname lookup fails; instead of an ambiguous name for possibly multiple unknown hostnames, the calling application is expected to have rules to deal with an unknown hostname in this case. @throws SecurityException If hostname lookup is not allowed. */ public static String Full_Hostname ( String hostname ) { try {return InetAddress.getByName (hostname).getCanonicalHostName ();} catch (UnknownHostException exception) {} return null; } /** Get the IP address of the host system.

@return The String representation of the IP address of the local system. If the IP address can not be determined, the {@link #DEFAULT_HOSTNAME} is returned. @see #IP_ADDRESS */ public static String IP_Address () {return IP_ADDRESS;} /** Get the IP address for a hostname.

@param hostname The name host system or the textual representation of its IP address. If null, the local host system is assumed. @return The String representation of the IP address for the hostname. This will be null if no IP address for the hostname could be found. N.B.: Unlike for the {@link #IP_Address() local system}, the {@link #DEFAULT_HOSTNAME} will not be returned if hostname lookup fails; instead of an ambiguous name for possibly multiple unknown hostnames, the calling application is expected to have rules to deal with an unknown hostname in this case. @throws SecurityException If hostname lookup is not allowed. */ public static String IP_Address ( String hostname ) { try {return InetAddress.getByName (hostname).getHostAddress ();} catch (UnknownHostException exception) {} return null; } /** Get the name of the host operating system. @return The name of the host OS from the os.name system property. */ public static String OS_Name () {return System.getProperty ("os.name");} /** Get all the environment variables from the host. @return A Properties object contain all of the environment variables. */ public static Properties Environment () { String get_env = "env", // Unix. OS = OS_Name ().toLowerCase (); // Check for MS/Windows special cases. if (OS.indexOf ("windows 9") != -1) get_env = "command.com /c set"; else if (OS.indexOf ("nt") != -1 || OS.indexOf ("windows 20") != -1 || OS.indexOf ("windows xp") != -1) get_env = "cmd.exe /c set"; Properties environment = new Properties (); try {environment.load (Runtime.getRuntime ().exec (get_env).getInputStream ());} catch (IOException exception) {/* Shouldn't happen */} return environment; } } pirl-2.3.8/PIRL/Utilities/tests/0000755000175000017500000000000012052546514016233 5ustar mathieumathieupirl-2.3.8/PIRL/Utilities/tests/test_Host.java0000644000175000017500000000266611742734660021072 0ustar mathieumathieu/* test_Host PIRL CVS ID: test_Host.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Host; public class test_Host { public static void main ( String[] arguments ) { System.out.println ("*** test_Host: " + Host.ID); System.out.println ("Short hostname: " + Host.SHORT_HOSTNAME); System.out.println (" Full hostname: " + Host.FULL_HOSTNAME); System.out.println (" IP address: " + Host.IP_ADDRESS); System.out.println ("Environment:"); Host.Environment ().list (System.out); } } pirl-2.3.8/PIRL/Utilities/tests/test_Authentication.java0000644000175000017500000002107511742734660023127 0ustar mathieumathieu/* test_Authentication PIRL CVS ID: test_Authentication.java,v 1.5 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Authentication; import java.security.KeyPair; public class test_Authentication { public static boolean Verbose = false; //Test case 1 /* Check to see if a string password passes */ public static boolean test_1 () { try { if (Verbose) System.out.println ("\ntest_1 -"); String password = "password"; if (Verbose) System.out.println (" password = " + password + '\n' +" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys(); if (Verbose) System.out.println (" encryption_key = Authentication.Public_Key (keys)"); String encryption_key = Authentication.Public_Key (keys); if (Verbose) System.out.println (" " + encryption_key + '\n' +" encrypted_key = " +"Authentication.Encoded_Password (password, encryption_key)"); String encrypted_key = Authentication.Encoded_Password (password, encryption_key); if (Verbose) System.out.println (" " + encrypted_key + '\n' +" Authentication.Authenticate (keys, encrypted_key, password)"); boolean result = Authentication.Authenticate (keys, encrypted_key, password); if (Verbose) System.out.println (" " + result); return result; } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return false; } //Test case 2 /* Check to see if a wrong string passes the authenticate check */ public static boolean test_2 () { try { if (Verbose) System.out.println ("\ntest_2 -"); String password = "incorrect_password"; if (Verbose) System.out.println (" password = " + password + '\n' +" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys(); if (Verbose) System.out.println (" encryption_key = Authentication.Public_Key (keys)"); String encryption_key = Authentication.Public_Key(keys); if (Verbose) System.out.println (" " + encryption_key + '\n' +" encrypted_key = " +"Authentication.Encoded_Password (password, encryption_key)"); String encrypted_key = Authentication.Encoded_Password(password, encryption_key); if (Verbose) System.out.println (" " + encrypted_key + '\n' +" Authentication.Authenticate " +"(keys, encrypted_key, \"correct_password\")"); // Should fail. boolean result = Authentication.Authenticate (keys, encrypted_key, "correct_password"); if (Verbose) System.out.println (" " + result); result = ! result; return result; } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return false; } /* Test 3 - test with different characters in the password */ public static boolean test_3 () { try { if (Verbose) System.out.println ("\ntest_3 -"); String password = "12akdgva3898sdvh_!@#$%^&*(()"; if (Verbose) System.out.println (" password = " + password + '\n' +" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys(); if (Verbose) System.out.println (" encryption_key = Authentication.Public_Key (keys)"); String encryption_key = Authentication.Public_Key(keys); if (Verbose) System.out.println (" " + encryption_key + '\n' +" encrypted_key = " +"Authentication.Encoded_Password (password, encryption_key)"); String encrypted_key = Authentication.Encoded_Password (password, encryption_key); if (Verbose) System.out.println (" " + encrypted_key + '\n' +" Authentication.Authenticate (keys, encrypted_key, password)"); boolean result = Authentication.Authenticate (keys, encrypted_key, password); if (Verbose) System.out.println (" " + result); return result; } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return false; } /* test_4 - try to serialize a null encryption key */ public static boolean test_4 () { if (Verbose) System.out.println ("\ntest_4 -"); boolean result = false; try { if (Verbose) System.out.println (" Authentication.Public_Key (null)"); String encryption_key = Authentication.Public_Key (null); if (Verbose) System.out.println (" " + encryption_key); result = (encryption_key == null); } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return result; } /* test 5 - try to encrypt a null password */ public static boolean test_5 () { if (Verbose) System.out.println ("\ntest_5 -"); boolean result = false; try { if (Verbose) System.out.println (" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys(); if (Verbose) System.out.println (" encryption_key = Authentication.Public_Key (keys)"); String encryption_key = Authentication.Public_Key (keys); if (Verbose) System.out.println (" " + encryption_key + '\n' +" encrypted_key = " +" Authentication.Encoded_Password (null, encryption_key)"); String encrypted_key = Authentication.Encoded_Password (null, encryption_key); if (Verbose) System.out.println (" " + encrypted_key); result = (encrypted_key == null); } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return result; } /* test 6 - try to encrypt using a nonsensical key */ public static boolean test_6 () { if (Verbose) System.out.println ("\ntest_6 -"); boolean result = false; try { if (Verbose) System.out.println (" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys (); if (Verbose) System.out.println (" encryption_key = Authentication.Public_Key (keys)"); String encryption_key = Authentication.Public_Key (keys); if (Verbose) System.out.println (" " + encryption_key + '\n' +" Authentication.Encoded_Password (\"password\", \"nonsense\")"); String encrypted_key = Authentication.Encoded_Password ("password", "nonsense"); if (Verbose) System.out.println (" " + encrypted_key); result = (encrypted_key == null); } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return result; } /* test 7 - try to decrypt using a nonsensical key */ public static boolean test_7 () { if (Verbose) System.out.println ("\ntest_7 -"); boolean result = false; try { if (Verbose) System.out.println (" keys = Authentication.Keys()"); KeyPair keys = Authentication.Keys (); if (Verbose) System.out.println (" Authentication.Authenticate (keys, " +"\"nonsense\", \"password\")"); // Should fail. result = Authentication.Authenticate (keys, "nonsense", "password"); if (Verbose) System.out.println (" " + result); result = ! result; } catch (Exception exception) { System.out.println ("!!! Exception: " + exception); } return result; } private static int correct_checks = 0, total_checks = 0; public static void check ( boolean passed, String report ) { total_checks++; if (passed) { correct_checks++; System.out.println ("PASS: " + report); } else System.out.println ("FAIL: " + report); } public static void main(String [] args) { if (args.length > 0 && args[0].startsWith ("-v")) Verbose = true; check (test_1 (), "Simple password string does Authenticate."); check (test_2 (), "Incorrect encoded password string does not Authenticate."); check (test_3 (), "Special characters in the password string do Autenticate."); check (test_4 (), "Public_Key (null) returns null."); check (test_5 (), "Encoded_Password (null, public_key) returns null."); check (test_6 (), "Encoded_Password (\"password\", \"nonsense\") returns null."); check (test_7 (), "Authenticate (keys, \"nonsense\", \"password\") fails."); System.out.println ("\n" + "Checks: " + total_checks + '\n' + "Passed: " + correct_checks); } } pirl-2.3.8/PIRL/Utilities/tests/test_Streams.java0000644000175000017500000000414211742734660021562 0ustar mathieumathieu/* test_Streams PIRL CVS ID: test_Streams.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Streams; import java.io.InputStream; public class test_Streams { public static void main ( String[] arguments ) { System.out.println ("*** test_Streams: " + Streams.ID); InputStream stream = null; try { System.out.println ("--- Streams.Get_Stream (\"test_Streams.java\")"); stream = Streams.Get_Stream ("test_Streams.java"); if (stream == null) System.out.println ("!!! FAIL"); else System.out.println ("*** PASS"); System.out.println ("--- Streams.Get_Stream (\"ftp://pirlftp.lpl.arizona.edu/pub/Java/PIRL.tar.gz\")"); stream = Streams.Get_Stream ("ftp://pirlftp.lpl.arizona.edu/pub/Java/PIRL.tar.gz"); if (stream == null) System.out.println ("!!! FAIL"); else System.out.println ("*** PASS"); System.out.println ("--- Streams.Get_Stream (\"no_such_file\")"); stream = Streams.Get_Stream ("no_such_file"); if (stream == null) System.out.println ("*** PASS"); else System.out.println ("!!! FAIL"); } catch (Exception exception) { System.out.println ("\n" +"Exception: " + exception + '\n' +" " + exception.getMessage ()); } } } pirl-2.3.8/PIRL/Utilities/tests/test_File.java0000644000175000017500000001765311742734660021036 0ustar mathieumathieu/* test_File PIRL CVS ID: test_File.java,v 1.12 2012/04/16 06:18:24 castalia Exp Copyright (C) 2006-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import java.io.IOException; import java.io.FileWriter; import java.net.URI; import PIRL.Configuration.Configuration; import PIRL.PVL.*; import PIRL.Utilities.Checker; import PIRL.Utilities.File; import PIRL.Strings.String_Buffer; public class test_File { public static void main ( String[] args ) { int status = 0; Checker checker = new Checker(); Configuration configuration = null; String test_Canonical_Directory = "test_dir"; String test_Logical_Link = "test_link"; String Canonical_Path = "/my/canonical/path/name", Logical_Path = "/the/logical/path", File_Name = "foo.txt"; try { // Set up the configuration String config_list = "GROUP = " + File.PATHNAMES_MAP_GROUP + "\n" + Canonical_Path + "=" + Logical_Path + "\n" + "END_GROUP\n" + "END"; Parameter parameter = new Parameter(File.PATHNAMES_MAP_GROUP, (Parser) (new Parser(config_list).Strict(false) .Crosshatch_Comments(true).Filter_Input(false))); configuration = new Configuration(parameter); String file_a = Canonical_Path + "/" + File_Name; System.out.println("Constructor Tests:"); //String tests ==================================================== File myfile = new File(file_a); checker.Check("File.Logical_Path(), from new File(String)", Canonical_Path + "/" + File_Name, myfile.Logical_Path()); myfile = new File(file_a).Pathnames_Map(configuration); checker.Check("File.Logical_Path(), from new File(String).Pathnames_Map(Configuration)", Logical_Path + "/" + File_Name, myfile.Logical_Path()); //URI tests URI fileuri = new URI("file:///my/canonical/path/name/foo.txt"); myfile = new File(fileuri); checker.Check("File.Logical_Path(), from new File(URI)", Canonical_Path + "/" + File_Name, myfile .Logical_Path()); myfile = new File(fileuri).Pathnames_Map(configuration); checker.Check("File.Logical_Path, from new File(URI).Pathnames_Map(Configuration)", Logical_Path + "/" + File_Name, myfile.Logical_Path()); // String Parent, String Child constructor tests myfile = new File(Canonical_Path, File_Name); checker.Check("File.Logical_Path(), from new File(String Parent, String Child)", Canonical_Path + "/" + File_Name, myfile.Logical_Path()); myfile = new File(Canonical_Path, File_Name) .Pathnames_Map(configuration); checker.Check("File.Logical_Path(), from new File(String Parent, String Child).Pathnames_MapConfiguration)", Logical_Path + "/" + File_Name, myfile.Logical_Path()); // File Parent, String Child constructor tests File parent_file = new File(Canonical_Path); myfile = new File(parent_file, File_Name); checker.Check("File.Logical_Path(), from new File(File Parent, String Child)", Canonical_Path + "/" + File_Name, myfile.Logical_Path()); myfile = new File(parent_file, File_Name).Pathnames_Map(configuration); checker.Check("File.Logical_Path(), from new File(File Parent, String Child).Pathnames_Map(Configuration)", Logical_Path + "/" + File_Name, myfile.Logical_Path()); // No matching path test myfile = new File("/this/path/will/not/match"); checker.Check("No matching Canonical Path Test", "/this/path/will/not/match", myfile.Logical_Path()); // Real file test myfile = new File("PIRL_Utilities_File_test_file.txt"); if (!myfile.exists()) { myfile.createNewFile(); // if the file doesn't exist then delete it when the application exits myfile.deleteOnExit(); } checker.Check( "Real File Test, no matching logical path", myfile.getCanonicalPath(), myfile.Logical_Path()); // Real file test with matching logical path File directory = new File(test_Canonical_Directory); String working_directory = (new File(directory.getCanonicalPath())).getParent(); if(!directory.mkdir()) throw new IOException("Cannot create test directory " + directory.getName()); //delete the directory on exit directory.deleteOnExit(); myfile = new File(directory,File_Name); myfile.deleteOnExit(); if(!myfile.createNewFile()) throw new IOException("Cannot create test file " + myfile.getPath()); // Create a symbolic link using a system call, since there is no // native java implementation of symbolic links. String symlink_command = "ln -s " + test_Canonical_Directory + " " + test_Logical_Link; Process p = Runtime.getRuntime().exec(symlink_command); if(p.waitFor() != 0) throw new IOException("Symlink creation failed for: " + symlink_command); p.destroy(); // Create a file object for the newly created symlink and delete it // on exit. File symlink_file = new File(test_Logical_Link); symlink_file.deleteOnExit(); checker.Check("logical symbolic link exists",true, symlink_file.exists()); config_list = "GROUP = " + File.PATHNAMES_MAP_GROUP + "\n" + test_Canonical_Directory + "=" + test_Logical_Link +"\n" + "END_GROUP\n" + "END"; parameter = new Parameter(File.PATHNAMES_MAP_GROUP, (Parser) (new Parser(config_list) .Strict(false) .Crosshatch_Comments(true) .Filter_Input(false))); configuration = new Configuration(parameter); myfile = myfile.Pathnames_Map(configuration); String Expected_Logical_Path = working_directory + "/" + test_Logical_Link + "/" + File_Name; checker.Check("local file logical path test",Expected_Logical_Path, myfile.Logical_Path()); checker.Check("File.exists()", true, myfile.exists()); checker.Check("File.canRead()", true, myfile.canRead()); checker.Check("File.canWrite()", true, myfile.canWrite()); //Configuration tests System.out.println("\nConfiguration Tests:"); config_list = "GROUP = " + File.PATHNAMES_MAP_GROUP + "\n" + Canonical_Path + "=" + Logical_Path + "\n" + "END_GROUP\n" + "END"; parameter = new Parameter(File.PATHNAMES_MAP_GROUP, (Parser) (new Parser(config_list) .Strict(false) .Crosshatch_Comments(true) .Filter_Input(false))); configuration = new Configuration(parameter); myfile = new File(Canonical_Path,File_Name).Pathnames_Map(configuration); checker.Check("File.getAbsolutePath",Canonical_Path + "/" + File_Name,myfile.getAbsolutePath()); File config_file = new File(File.DEFAULT_CONFIGURATION_FILENAME); if(!config_file.createNewFile()) throw new IOException("Default configuration file: " + File.DEFAULT_CONFIGURATION_FILENAME + " exists.\n"); config_file.deleteOnExit(); FileWriter filewriter = new FileWriter(config_file); filewriter.write(config_list); filewriter.close(); myfile = new File(file_a); checker.Check("default configuration file test", Logical_Path + "/" + File_Name, myfile.Logical_Path()); //System.out.println("File Name: " + myfile.getName()); //System.out.println("The absolute path is: " + myfile.getAbsolutePath()); //System.out.println("The logical path is: " + myfile.Logical_Path()); } catch (Exception exception) { status = 1; System.err.println("\n!!! Unexpected Exception:\n" + exception + exception.getMessage()); } System.out.println("\nChecks: " + checker.Checks_Total ); System.out.println("Passed: " + checker.Checks_Passed); System.exit(status); } } pirl-2.3.8/PIRL/Utilities/tests/Host_Address.java0000644000175000017500000000704611742734660021475 0ustar mathieumathieu/* Host_Address CVS ID: Host_Address.java,v 1.3 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Host; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JLabel; import javax.swing.JTextField; import java.awt.Dimension; import java.awt.BorderLayout; import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Insets; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class Host_Address extends JFrame { JTextField Hostname_Field = new JTextField (Host.FULL_HOSTNAME), IP_Address_Field = new JTextField (Host.IP_ADDRESS); public Host_Address () { super ("Host Address"); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Hostname. location.anchor = GridBagConstraints.EAST; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (5, 5, 5, 3); panel.add (new JLabel ("Hostname:"), location); Dimension dimension = Hostname_Field.getPreferredSize (); dimension.width = 175; Hostname_Field.setMinimumSize (dimension); Hostname_Field.setPreferredSize (dimension); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (5, 0, 5, 5); panel.add (Hostname_Field, location); Hostname_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Reset_from_Name (Hostname_Field.getText ());}}); // IP Address. location.anchor = GridBagConstraints.EAST; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (5, 5, 5, 3); panel.add (new JLabel ("IP Address:"), location); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (5, 0, 5, 5); panel.add (IP_Address_Field, location); IP_Address_Field.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Reset_from_Name (IP_Address_Field.getText ());}}); getContentPane ().add (panel, BorderLayout.CENTER); setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); pack (); } private void Reset_from_Name ( String hostname ) { Hostname_Field.setText (Host.Full_Hostname (hostname)); IP_Address_Field.setText (Host.IP_Address (hostname)); } public static void main (String[] args) { Host_Address host_address = new Host_Address (); host_address.setLocation (250, 300); //host_address.setPreferredSize (new Dimension (100, 50)); host_address.setVisible (true); } } pirl-2.3.8/PIRL/Utilities/tests/Makefile0000644000175000017500000000072711231203444017667 0ustar mathieumathieu# Makefile for Java classes JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = test_Host.class \ Host_Address.class \ test_File.class \ test_Streams.class \ test_ByteBuffer_Streams.class \ test_Styled_Multiwriter.class \ test_Authentication.class \ test_Hex_Coding.class \ test_UNIX_Process.class all: ${CLASSES} classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Utilities/tests/test_Hex_Coding.java0000644000175000017500000000475611742734660022166 0ustar mathieumathieu/* test_Hex_Coding PIRL CVS ID: test_Hex_Coding.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Authentication; public class test_Hex_Coding { public static void main ( String[] args ) { String expected_string = "000111FF", obtained_string; byte[] expected_bytes = {0x00, 0x01, 0x11, (byte)0xFF}, obtained_bytes; boolean passed; obtained_string = Authentication.Encode_Hex (expected_bytes); passed = expected_string.equals (obtained_string); System.out.println ((passed ? "PASS" : "FAIL") + ": Encode_Hex"); if (! passed) System.out.println ("====> expected: >|" + expected_string + "|<\n" +"====> obtained: >|" + obtained_string + "|<"); obtained_bytes = Authentication.Decode_Hex (expected_string); passed = true; if (obtained_bytes.length != expected_bytes.length) passed = false; else { for (int index = 0; index < obtained_bytes.length; index++) { if (obtained_bytes[index] != expected_bytes[index]) { passed = false; break; } } } System.out.println ((passed ? "PASS" : "FAIL") + ": Decode_Hex"); if (! passed) { System.out.print ("====> expected:"); for (int index = 0; index < expected_bytes.length; index++) System.out.print (" " + Integer.toHexString ((int)expected_bytes[index] & 0xFF) .toUpperCase ()); System.out.println (); System.out.print ("====> obtained:"); for (int index = 0; index < obtained_bytes.length; index++) System.out.print (" " + Integer.toHexString ((int)obtained_bytes[index] & 0xFF) .toUpperCase ()); System.out.println (); } System.exit (0); } } pirl-2.3.8/PIRL/Utilities/tests/test_Styled_Multiwriter.java0000644000175000017500000000616611742734660024027 0ustar mathieumathieu/* test_Styled_Multiwriter PIRL CVS ID: test_Styled_Multiwriter.java,v 1.3 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.Styled_Multiwriter; import PIRL.Utilities.Multiwriter_IOException; import PIRL.Utilities.Checker; import java.io.StringWriter; import java.util.Iterator; public class test_Styled_Multiwriter { public static void main ( String[] arguments ) { System.out.println ("*** test_Styled_Multiwriter:\n" + Styled_Multiwriter.ID); Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; int status = 0; try { if (checker.Verbose) System.out.println ("--- Constructor"); Styled_Multiwriter multiwriter = new Styled_Multiwriter (); StringWriter writer_0 = new StringWriter (), writer_1 = new StringWriter (), writer_2 = new StringWriter (); if (checker.Verbose) System.out.println ("--- Add"); checker.Check ("Add (null)", false, multiwriter.Add (null)); checker.Check ("Add (writer_0)", true, multiwriter.Add (writer_0)); checker.Check ("Add (writer_1)", true, multiwriter.Add (writer_1)); checker.Check ("Add (writer_2)", true, multiwriter.Add (writer_2)); checker.Check ("Add (writer_2) again", false, multiwriter.Add (writer_2)); checker.Check ("Writers ().size ()", 3, multiwriter.Writers ().size ()); String expected = "Test string"; if (checker.Verbose) System.out.println ("--- write (\"" + expected + "\")"); multiwriter.write (expected); checker.Check ("write (\"" + expected + "\")", expected, writer_0.toString ()); checker.Check ("write (\"" + expected + "\")", expected, writer_1.toString ()); checker.Check ("write (\"" + expected + "\")", expected, writer_2.toString ()); } catch (Multiwriter_IOException exception) { System.out.println (exception + "\n" + exception.Exceptions.size () + " exceptions -"); Iterator exceptions = exception.Exceptions.iterator (); while (exceptions.hasNext ()) System.out.println (exceptions.next ()); System.out.println (); exception.printStackTrace (System.out); status = 1; } System.out.println ("\nChecks: " + checker.Checks_Total ); System.out.println ("Passed: " + checker.Checks_Passed); System.exit (status); } } pirl-2.3.8/PIRL/Utilities/tests/test_UNIX_Process.java0000644000175000017500000000500211742734660022421 0ustar mathieumathieu/* test_UNIX_Process PIRL CVS ID: test_UNIX_Process.java,v 1.3 2012/04/16 06:18:24 castalia Exp Copyright (C) 2009-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.UNIX_Process; import java.io.*; public class test_UNIX_Process { private static final String TEST_COMMAND = "sleep"; private static int run_time = 3, wait_time = 1; public static void main ( String[] args ) { if (args.length > 0) { try {run_time = Integer.parseInt (args[0]);} catch (NumberFormatException exception) {Usage ();} } if (args.length > 1) { try {wait_time = Integer.parseInt (args[1]);} catch (NumberFormatException exception) {Usage ();} } String command = TEST_COMMAND + " " + run_time; System.out.println ("Executing process: " + command); UNIX_Process process = null; try {process = new UNIX_Process (Runtime.getRuntime ().exec (command));} catch (Exception exception) { System.out.println ("Unable to execute the process.\n" + exception.getMessage ()); System.exit (2); } System.out.println ("Process ID = " + process.ID ()); System.out.println ("Waiting " + wait_time + " second" + ((wait_time == 1) ? "" : "s") + " for the process to exit..."); try { int exit_status = process.waitFor (wait_time * 1000); System.out.println ("Process exit status: " + exit_status); } catch (Exception exception) { System.out.println ("An exception was thrown: " + exception); // Kill the process. System.out.println ("Destroying the process."); process.destroy (); } System.exit (0); } public static void Usage () { System.out.println ("Usage: test_UNIX_Process [ []]"); System.exit (1); } } pirl-2.3.8/PIRL/Utilities/tests/test_ByteBuffer_Streams.java0000644000175000017500000000600011742734660023672 0ustar mathieumathieu/* test_ByteBuffer_Streams PIRL CVS ID: test_ByteBuffer_Streams.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Utilities.ByteBuffer_InputStream; import PIRL.Utilities.ByteBuffer_OutputStream; import PIRL.PVL.*; import PIRL.Utilities.Checker; import java.nio.ByteBuffer; public class test_ByteBuffer_Streams { public static void main ( String[] arguments ) { System.out.println ("*** test_ByteBuffer_Streams:\n" +" " + ByteBuffer_InputStream.ID + '\n' +" " + ByteBuffer_OutputStream.ID); String parameters_string = "Parameter = One\n" + "Parameter = Two\n" + "GROUP = Test\n" + " Parameter = 1\n" + " OBJECT = SUB_A\n" + " SUB_A1 = 1.0\n" + " SUB_A2 = 2.0\n" + " SUB_A3 = 3.0\n" + " END_OBJECT\n" + " GROUP = SUB_B\n" + " SUB_B1 = one\n" + " SUB_B2 = \n" + " (two, 2, to, too)\n" + " END_GROUP\n" + "END_GROUP\n" + "END\n"; Parameter string_parameters, buffer_parameters; ByteBuffer buffer = ByteBuffer.allocate (8192); byte[] bytes; Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; try { if (checker.Verbose) System.out.println ("--- parameters_string:\n" + parameters_string); string_parameters = new Parameter (new Parser (parameters_string)); buffer.put (parameters_string.getBytes ()); buffer.flip (); buffer_parameters = new Parameter (new Parser (new ByteBuffer_InputStream (buffer))); checker.Check ("Parameter Parsed from ByteBuffer_InputStream", string_parameters.Description (), buffer_parameters.Description ()); buffer.rewind (); bytes = new byte[buffer.limit ()]; buffer.put (bytes); buffer.clear (); string_parameters.Write (new ByteBuffer_OutputStream (buffer)); buffer.flip (); bytes = new byte[buffer.limit ()]; buffer.get (bytes, 0, buffer.limit ()); checker.Check ("Parameter Written to ByteBuffer_OutputStream", parameters_string, new String (bytes, 0, buffer.limit ())); } catch (Exception exception) { System.out.println ("\n" +"Exception: " + exception + '\n' +" " + exception.getMessage ()); } } } pirl-2.3.8/PIRL/Utilities/Makefile0000644000175000017500000000132411231203514016515 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.18 2009/07/21 00:09:48 castalia Exp # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm JPATH ?= ../..:$(JCM) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Streams.class \ ByteBuffer_InputStream.class \ ByteBuffer_OutputStream.class \ Host.class \ File.class \ Integer_Range.class \ Real_Range.class \ Checker.class \ Styled_Writer.class \ Multiwriter_IOException.class \ Suspendable_Styled_Writer.class \ Styled_Multiwriter.class \ Authentication.class \ UNIX_Process.class all: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Utilities/ByteBuffer_InputStream.java0000644000175000017500000001636011742734657022346 0ustar mathieumathieu/* ByteBuffer_InputStream PIRL CVS ID: ByteBuffer_InputStream.java,v 1.4 2012/04/16 06:18:23 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.InvalidMarkException; import java.io.InputStream; import java.io.IOException; /** A ByteBuffer_InputStream presents a ByteBuffer as an InputStream.

This class is thread safe: Methods that might modify the backing ByteBuffer are synchronized on the ByteBuffer.

@author Bradford Castalia, UA/PIRL @version 1.4 @see ByteBuffer @see InputStream */ public class ByteBuffer_InputStream extends InputStream { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.ByteBuffer_InputStream (1.4 2012/04/16 06:18:23)"; private ByteBuffer Source; private static String NL; static { if ((NL = System.getProperty ("line.separator")) == null) NL = "\n"; } /*============================================================================== Constructors */ /** Construct a ByteBuffer_InputStream on a ByteBuffer.

@param source The ByteBuffer to be used as the source of input data. */ public ByteBuffer_InputStream ( ByteBuffer source ) { if ((Source = source) == null) throw new NullPointerException (ID + NL + "A buffer must be provided."); } /** A ByteBuffer_InputStream must have a ByteBuffer. */ private ByteBuffer_InputStream () {} /*============================================================================== Accessors */ /** Get the ByteBuffer backing this ByteBuffer_InputStream.

@return The ByteBuffer backing this ByteBuffer_InputStream. */ public ByteBuffer Source () {return Source;} /*============================================================================== InputStream */ /** Get the number of bytes remaining in the source.

@return The number of bytes remaining in the {@link #Source() source}. This is the difference between the buffer's current input {@link Buffer#position() position} and the input {@link Buffer#limit() limit}. Note that the limit may be less than the buffer {@link Buffer#capacity() capacity}. */ public int available () {return Source.remaining ();} /** Sets a mark at the current input position.

The operation is synchronized on the source buffer.

@param read_limit Ignored: The read limit is always the {@link #Source() source} limit. */ public void mark ( int read_limit ) {synchronized (Source) {Source.mark ();}} /** Test if stream {@link #mark(int) mark} and {@link #reset() reset} are supported.

@return Always returns true. */ public boolean markSupported () {return true;} /** Get the next datum from the source.

The operation is synchronized on the source buffer.

@return The value of the next {@link #Source() source} byte (0-255) at the current buffer position, or -1 if the {@link #Source() source} position is at its {@link Buffer#limit() limit}. */ public int read () { synchronized (Source) { if (Source.hasRemaining ()) return ((int)Source.get () & 0xFF); } return -1; } /** Get some number of bytes from the source and store them into a byte array.

This method simply returns read (byte_array, 0, byte_array.length).

@param byte_array A byte[] array where the source data is to be stored. @return The number of bytes read into the array, or -1 if the {@link #Source() source} position is at its {@link Buffer#limit() limit}. @throws NullPointerException If the byte array is null. @see #read(byte[], int, int) */ public int read ( byte[] byte_array ) {return read (byte_array, 0, byte_array.length);} /** Get some number of bytes from the source and store them into a byte array.

If the length to read is less than or equal to zero, zero is returned. Otherwise the effective length is the lesser of the length and the amount of data {@link ByteBuffer#remaining() remaining} in the {@link #Source() source} buffer.

The operation is synchronized on the source buffer.

@param byte_array A byte[] array where the source data is to be stored. @param offset The index of the array where the first byte will be stored. @param length The maximum number of bytes to transfer from the {@link #Source() source} to the array. @return The number of bytes read into the array, or -1 if the {@link #Source() source} position is at its {@link Buffer#limit() limit}. @throws NullPointerException If the byte array is null. @throws IndexOutOfBoundsException If the offset is negative or the offset plus the effective length is greater than the remaining space in the array. */ public int read ( byte[] byte_array, int offset, int length ) { synchronized (Source) { if (! Source.hasRemaining ()) return -1; if (byte_array == null) throw new NullPointerException (ID + NL + "Can't read into a null array."); if (length <= 0) return 0; if (length > Source.remaining ()) length = Source.remaining (); if (offset < 0 || (offset + length) > byte_array.length) throw new IndexOutOfBoundsException (ID + NL + "Can't read into the array of length " + byte_array.length + " starting at offset " + offset + NL + ((length == Source.remaining ()) ? "the remaining " : "") + length + " buffer byte" + ((length == 1) ? "" : "s") + '.'); Source.get (byte_array, offset, length); } return length; } /** Reset the {@link #Source() source} position to the most recent {@link #mark(int) mark} position.

The operation is synchronized on the source buffer.

@throws IOException If no mark has been set. */ public void reset () throws IOException { try {synchronized (Source) {Source.reset ();}} catch (InvalidMarkException exception) { throw new IOException (ID + NL + "No mark set for reset."); } } /** Skip over some number of input bytes.

The {@link #Source() source} input position is moved to its current position plus the lesser of the amount specified or the amount available data {@link ByteBuffer#remaining() remaining} in the buffer.

The operation is synchronized on the source buffer.

@param amount The amount of data to skip. @return The number of data bytes skipped. */ public long skip ( long amount ) { synchronized (Source) { if (amount > Source.remaining ()) amount = Source.remaining (); if (amount > 0) Source.position (Source.position () + (int)amount); } return amount; } } pirl-2.3.8/PIRL/Utilities/ByteBuffer_OutputStream.java0000644000175000017500000001427611742734660022545 0ustar mathieumathieu/* ByteBuffer_OutputStream PIRL CVS ID: ByteBuffer_OutputStream.java,v 1.3 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.nio.Buffer; import java.nio.ByteBuffer; import java.io.OutputStream; import java.io.EOFException; /** A ByteBuffer_OutputStream presents a ByteBuffer as an OutputStream.

This class is thread safe: Methods that might modify the backing ByteBuffer are synchronized on the ByteBuffer.

@author Bradford Castalia, UA/PIRL @version 1.3 @see ByteBuffer @see OutputStream */ public class ByteBuffer_OutputStream extends OutputStream { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Conductor.ByteBuffer_OutputStream (1.3 2012/04/16 06:18:24)"; private ByteBuffer Destination; private static String NL; static { if ((NL = System.getProperty ("line.separator")) == null) NL = "\n"; } /*============================================================================== Constructors */ /** Construct a ByteBuffer_OutputStream on a ByteBuffer.

@param destination The ByteBuffer to be used as the destination of output data. @throws NullPointerException If the destination is null. */ public ByteBuffer_OutputStream ( ByteBuffer destination ) { if ((Destination = destination) == null) throw new NullPointerException (ID + NL + "A buffer must be provided."); } /** A ByteBuffer_OutputStream must have a ByteBuffer. */ private ByteBuffer_OutputStream () {} /*============================================================================== Accessors */ /** Get the ByteBuffer backing this ByteBuffer_OutputStream.

@return The ByteBuffer backing this ByteBuffer_OutputStream. */ public ByteBuffer Destination () {return Destination;} /*============================================================================== OutputStream */ /** Get the amount of storage space remaining in the destination.

@return The number of bytes remaining in the {@link #Destination() destination}. This is the difference between the buffer's current output {@link Buffer#position() position} and the output {@link Buffer#limit() limit}. Note that the limit may be less than the buffer {@link Buffer#capacity() capacity}. */ public int available () {return Destination.remaining ();} /** Put the datum at the next position in the destination.

The operation is synchronized on the destination buffer.

@throws EOFExcetption If the destination is full; i.e. the output position is at its limit. @see #available() */ public void write ( int datum ) throws EOFException { synchronized (Destination) { if (Destination.hasRemaining ()) { Destination.put ((byte)datum); return; } } throw new EOFException (ID + NL + "Can't write to a full buffer."); } /** Put the contents of a byte array into the destination.

This method simply uses write (byte_array, 0, byte_array.length).

@param byte_array A byte[] array containing the data to be stored in the destination buffer. @throws NullPointerException If the byte array is null. @throws EOFExcetption If the destination is full; i.e. the output position is at its limit. @see #write(byte[], int, int) @see #available() */ public void write ( byte[] byte_array ) throws EOFException {write (byte_array, 0, byte_array.length);} /** Put some number of bytes from a byte array into the destination.

If the amount to write is less than or equal to zero nothing is done. The amount specified to write must be no greater than the number of bytes in the array after the offset nor the space remaining in the destination buffer.

The operation is synchronized on the destination buffer.

@param byte_array A byte[] array containing data to be transferred. @param offset The index of the array containing the first byte to be transferred. @param amount The maximum number of bytes to transfer from the array to the {@link #Destination() destination}. @throws NullPointerException If the byte array is null. @throws IndexOutOfBoundsException If the offset is negative or the offset plus the amount is greater than the remaining bytes in the array. @throws EOFExcetption If the space {@link #available() available} in the destination is less than the amount of data specified to transfer. */ public void write ( byte[] byte_array, int offset, int amount ) throws EOFException { synchronized (Destination) { if (byte_array == null) throw new NullPointerException (ID + NL + "Can't write from a null array."); if (amount <= 0) return; if (offset < 0 || (offset + amount) > byte_array.length) throw new IndexOutOfBoundsException (ID + NL + "Can't write from the array of length " + byte_array.length + " starting at offset " + offset + " the specified " + + amount + " byte" + ((amount == 1) ? "" : "s") + '.'); if (amount > Destination.remaining ()) throw new EOFException (ID + NL + "Can't write from the array of length " + byte_array.length + " starting at offset " + offset + " the specified " + + amount + " byte" + ((amount == 1) ? "" : "s") + NL + "because only " + Destination.remaining () + " byte" + ((Destination.remaining () == 1) ? "" : "s") + " are available in the buffer."); Destination.put (byte_array, offset, amount); } } } pirl-2.3.8/PIRL/Utilities/Multiwriter_IOException.java0000644000175000017500000000512411742734660022541 0ustar mathieumathieu/* Multiwriter_IOException PIRL CVS ID: Multiwriter_IOException.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.io.IOException; import java.util.Vector; /** A Multiwriter_IOException is an IOException that may carry multiple IOExceptions from multiple exception sources.

This exception is used when an IOExceptions might occur from more than one source but the first occurance of an exception will not be thrown. In this case all the exceptions that occurred can be accumulated in a Multiwriter_IOException which is then thrown after processing of all the sources has been completed. Each IOException is paired with the source of the exception.

@author Bradford Castalia - UA/PIRL @version 1.4 @see PIRL.Utilities.Styled_Multiwriter */ public class Multiwriter_IOException extends IOException { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utilities.Multiwriter_IOException (1.4 2012/04/16 06:18:24)"; /** The list of source objects that have thrown an exception. */ public Vector Sources = new Vector (2); /** The list of IOExceptions that were thrown.

Each exception occurs at the same position in the list as the position of the source in the {@link #Sources} list that caused the exception. */ public Vector Exceptions = new Vector (2); /** Construct a Multiwriter_IOException with an explanatory message.

@param message A String explaining the exception. */ public Multiwriter_IOException ( String message ) {super (message);} /** Construct a Multiwriter_IOException with no explanatory message. */ public Multiwriter_IOException () {} } pirl-2.3.8/PIRL/Utilities/File.java0000644000175000017500000003771311742734660016634 0ustar mathieumathieu/* File PIRL CVS ID: File.java,v 1.16 2012/04/16 06:18:24 castalia Exp Copyright (C) 2006-2102 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; //Pirl Packages import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.PVL.Parameter; import PIRL.PVL.PVL_Exception; import PIRL.Strings.String_Buffer; import java.io.IOException; import java.net.URI; import java.util.Iterator; import java.util.Vector; /** This File extends the capabilities of the JFC {@link java.io.File#File(java.lang.String) File} to enable mapping the File's canonical path to a logical path. The mappings are defined by a {@link #Pathnames_Map(Configuration) pathnames map} obtained from a Configuration.

@see java.io.File @see PIRL.Configuration.Configuration @author Rodney Heyd UA/PIRL/HiRISE @version 1.16 */ public class File extends java.io.File { /** Class name and version identification. */ public static final String ID = "PIRL.Utilities.File (1.16 2012/04/16 06:18:24)"; /** Default configuration file name: "Pathname.conf". */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Pathnames_Map.conf"; /** The Parameter Group in the Configuration containing the logical pathname map. */ public static final String PATHNAMES_MAP_GROUP = "Pathnames_Map"; /** The canonical to logical path map. */ private Configuration Pathnames_Map = null; /** The logical path for this File. */ private String Logical_Path = null; /** Exit Status: Success. */ public static final int EXIT_SUCCESS = 0; /** Exit Status: Invalid command line syntax. */ public static final int EXIT_INVALID_COMMAND_LINE_SYNTAX = 1; /** Exit Status: A Configuration problem was encountered. */ public static final int EXIT_CONFIGURATION_PROBLEM = 2; /** Exit Status: An IO Error occured. */ public static final int EXIT_IO_ERROR = 3; // Debug control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_HELPERS = 1 << 4, DEBUG_MAIN = 1 << 5, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*=========================================================================== Constructors */ /** Construct a File from a String.

@param pathname A pathname String. @see java.io.File */ public File ( String pathname ) {super (pathname);} /** Construct a File from directory and filename Strings.

@param parent A directory pathname String. @param child A filename String for a file in the parent directory. @see java.io.File */ public File ( String parent, String child ) {super (parent, child);} /** Construct a File a directory File and a filename String.

@param parent A directory File. @param child A filename String for a file in the parent directory. @see java.io.File */ public File ( File parent, String child ) {super (parent, child);} /** Construct a File from a URI.

@param uri A URI. @see java.net.URI */ public File ( URI uri ) {super (uri);} /*=========================================================================== Accessors */ /** Set the pathnames map to be used for mapping canonical paths to logical paths.

The canonical to logical pathname mappings are defined by a {@link #PATHNAMES_MAP_GROUP} group of Assignment Parameters of the form:

<canonical segment> = <logical segment>

The cononical segment is any substring of a pathname that might be returned by the {@link java.io.File#getCanonicalPath()} method. The logical segment replaces the canonical segment wherever it occurs in the pathname to produce the {@link #Logical_Path()} of the File. Initially the logical path is identical to the canonical path, then all mapping parameters are applied in the order they occur in the group. Thus a matching canonical segment may have been produced, in whole or in part, by a preceeding canonical to logical segment mapping replacement.

If the configuration argument is null an attempt will be made to load a Configuration from the {@link #DEFAULT_CONFIGURATION_FILENAME} file. If this file can not be accessed a default Configuration is provided. N.B.: This behaviour is different than the {@link #Pathnames_Map(String)} method which will throw a Configuration_Exception if the source file can not be accessed.

If the configuration is named {@link #PATHNAMES_MAP_GROUP} then a copy of the entire configuration is used as the pathnames map. Otherwise the configuration searched for a Parameter Group with that name which is copied. If no {@link #PATHNAMES_MAP_GROUP} is found then an empty pathnames map is provided; no canonical to logical pathname mapping will be done in this case.

Any {@link Configuration#Defaults default} Configuration parameters are removed from the pathnames map as well as any Parameters that are a Group or have an Array, or no, Value.

@param configuration A Configuration to use as the source of the pathnames map. @return This File. @throws Configuration_Exception If there was a problem obtaining the {@link #PATHNAMES_MAP_GROUP} from the map or reading the {@link #DEFAULT_CONFIGURATION_FILENAME}. */ public File Pathnames_Map ( Configuration configuration ) throws Configuration_Exception { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> File.Pathnames_Map: " + configuration); Configuration map = null; if (configuration == null) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Attempting to load configuration file " + DEFAULT_CONFIGURATION_FILENAME); try {configuration = new Configuration (DEFAULT_CONFIGURATION_FILENAME);} catch (Configuration_Exception exception) { throw new Configuration_Exception (ID + '\n' +"Unable to obtain a Pathnames Map Configuration\n" +" from the \"" + DEFAULT_CONFIGURATION_FILENAME + "\" source file.\n" + exception.getMessage ()); } catch (IllegalArgumentException exception) { // The default file could not be found; use an empty map. if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" No default config file."); } } if (configuration != null) { if (configuration.Name ().equalsIgnoreCase (PATHNAMES_MAP_GROUP)) // Copy the entire Configuration. map = new Configuration (configuration); else { synchronized (configuration) { // Try to get the Pathnames_Map group. boolean case_sensitive = configuration.Case_Sensitive (false); try {map = configuration.Group (PATHNAMES_MAP_GROUP);} catch (Configuration_Exception exception) { configuration.Case_Sensitive (case_sensitive); throw new Configuration_Exception (ID + '\n' + "Unable to obtain the "+ PATHNAMES_MAP_GROUP + " from the Configuration.\n" + exception.getMessage ()); } configuration.Case_Sensitive (case_sensitive); } } if (map != null) { // Remove Configuration Defaults and inappropriate parameters. Parameter parameter; Iterator parameters = map.iterator (), defaults; Clean_Map: while (parameters.hasNext ()) { parameter = (Parameter)parameters.next (); try { if (parameter.Is_Aggregate () || parameter.Value () == null || parameter.Value ().Is_Array ()) { parameters.remove (); continue Clean_Map; } } catch (PVL_Exception exception) {/* Can't happen */} defaults = Configuration.Defaults.iterator (); while (defaults.hasNext ()) { if (parameter.Name () .equals (((Parameter)defaults.next ()).Name ())) { parameters.remove (); continue Clean_Map; } } } } } if (map == null) { // Use and empty map. if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Using an empty map."); Pathnames_Map = new Configuration ((Parameter)null); Pathnames_Map.Name (PATHNAMES_MAP_GROUP); } else Pathnames_Map = map; Logical_Path = null; // Reset. if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (Pathnames_Map.Description () + "<<< File.Pathnames_Map: " + configuration); return this; } /** Set the pathnames map from a configuration file source.

@param source The source of a configuration file. This may be a local filename, a URL, or a jar file resource name. @return This File. @throws Configuration_Exception If the source file could not be accessed (not found or no permission to read) or there was a problem constructing a Configuration from the source contents. @see #Pathnames_Map(Configuration) */ public File Pathnames_Map ( String source ) throws Configuration_Exception { Configuration configuration = null; if (source != null) { try {configuration = new Configuration (source);} catch (Configuration_Exception exception) { throw new Configuration_Exception (ID + '\n' +"Unable to obtain a Pathnames Map Configuration\n" +" from the \"" + source + "\" source file.\n" + exception.getMessage ()); } catch (IllegalArgumentException exception) { // The source file could not be found. throw new Configuration_Exception (ID + '\n' +"Couldn't access the Pathnames Map Configuration\n" +" from the \"" + source + "\" source file."); } } return Pathnames_Map (configuration); } /** Get the current pathnames map.

@return A Configuration containing the current pathnames map parameters. This may be null if no map has yet been assigned. */ public Configuration Pathnames_Map () {return Pathnames_Map;} /** Get the logical path for the file.

The logical path is based on the File's {@link java.io.File#getCanonicalPath() canonical path} with the cononical segments listed in thepathnames map replaced with the logical segments to which they are assigned.

N.B. @return The logical path String. @throws Configuration_Exception If a default pathnames map was needed but the source file could not be accessed (not found or no permission to read) or there was a problem constructing a Configuration from the source contents. @throws IOException If the @link java.io.File#getCanonicalPath() canonical path of the File could not be obtained. @see #Pathnames_Map(Configuration) @see #Pathnames_Map(String) */ public String Logical_Path () throws IOException, Configuration_Exception { if (Logical_Path == null) Set_Logical_Path (); return Logical_Path; } /*------------------------------------------------------------------------------ Helpers */ /** Sets the logical path.

The Pathnames_Map is used to determine the logical path mapping.

@see #Pathnames_Map(Configuration) */ private void Set_Logical_Path () throws IOException, Configuration_Exception { if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (">>> File.Set_Logical_Path"); if (Pathnames_Map == null) // Instantiate the default map. Pathnames_Map ((Configuration)null); String_Buffer logical_path = null; try { // Initialize with the canonical path of this File. logical_path = new String_Buffer (getCanonicalPath ()); } catch (IOException exception) { throw new IOException (ID + '\n' +"Unable to obtain the canonical path for \"" + getName () + "\"\n" + exception.getMessage ()); } if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (" Canonical path: " + logical_path); Parameter parameter; Iterator map_entries = Pathnames_Map.iterator (); while (map_entries.hasNext ()) { parameter = (Parameter)map_entries.next (); try { logical_path.replace (0, parameter.Name (), parameter.Value ().String_Data ()); if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (" Canonical segment: " + parameter.Name () + '\n' +" Logical segment: " + parameter.Value ().String_Data () + '\n' +" ---> Logical path: " + logical_path); } catch (PVL_Exception exception) {/* Shouldn't happen with clean map. */} } Logical_Path = logical_path.toString (); if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println ("<<< File.Set_Logical_Path: " + Logical_Path); } /*============================================================================== Application */ /** Map filenames to their logical forms.

The command line syntax is described by the {@link #Usage() Usage} method.

Each filename specified will be listed along with its canonical path and mapped logical path.

Exit Status Values

0 - Success
1 - Command line syntax problem
2 - A Configuration problem was encountered
3 - An IO Error occured

@param arguments Array of command line argument Strings. @see #Usage() */ public static void main ( String[] arguments ) { String configuration_filename = null; Vector filenames = new Vector (); for (int count = 0; count < arguments.length; ++count) { if (arguments[count].length () == 0) continue; if (arguments[count].charAt (0) == '-') { switch (arguments[count].charAt (1)) { case 'C': // -Configuration case 'c': String filename = null; if (++count == arguments.length || arguments[count].charAt (0) == '-') { System.err.println ("Missing Configuration filename.\n"); Usage (); } if (configuration_filename != null) { System.err.println ("Multiple configuration files specified:\n" +" " + configuration_filename + '\n' +" " + arguments[count]); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } configuration_filename = arguments[count]; break; default: System.err.println ("Unrecognized command line option: " + arguments[count] + '\n'); case 'H': // -Help case 'h': Usage (); } } else filenames.add (arguments[count]); } if (filenames.isEmpty ()) { System.out.println ("No filename specified.\n"); Usage (); } File logical_File; String canonical_path, logical_path; Iterator list = filenames.iterator (); try { while (list.hasNext ()) { logical_File = new File ((String)list.next ()) .Pathnames_Map (configuration_filename); canonical_path = logical_File.getCanonicalPath (); logical_path = logical_File.Logical_Path (); System.out.println (" Filename: " + logical_File.getPath () + '\n' +"Canonical path: " + canonical_path + '\n' +" Logical path: " + logical_path + '\n'); } } catch (Configuration_Exception exception) { System.err.println (exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } catch (IOException exception) { System.err.println (exception.getMessage ()); System.exit (EXIT_IO_ERROR); } System.exit (EXIT_SUCCESS); } /** Prints the command line usage syntax.

Usage: File <Options> <filename> [...]
  Options -
    [-Configuration <filename>
      (default: Pathnames_Map.conf)
    [-Help]
*/ public static void Usage () { System.out.println ( ID + '\n' + "Usage: File [...]\n" + " Options -\n" + " [-Configuration ]" + " (default: " + DEFAULT_CONFIGURATION_FILENAME + ")\n" + " [-Help]" ); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Utilities/Real_Range.java0000644000175000017500000002156011742734660017745 0ustar mathieumathieu/* Real_Range PIRL CVS ID: Real_Range.java,v 1.13 2012/04/16 06:18:24 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; /** A Real_Range specifies a range of integer values.

The range of values is characterized by minimum and maximum values that identify the inclusive limits of the range. Single-valued and open-ended ranges are defined. An appropriate String representation is provided.

@author Bradford Castalia, UA/PIRL @version 1.13 */ public class Real_Range implements Cloneable { public static final String ID = "PIRL.Utilities.Real_Range (1.13 2012/04/16 06:18:24)"; /** The range minimum value. */ protected double Minimum = 0.0; /** The range maximum value. */ protected double Maximum = 0.0; /** Constructs a default Real_Range.

The range values are initialized to zero. */ public Real_Range () {} /** Constructs a Real_Range from limiting values.

Note: If the minimum is greater than the maximum then it is used as the maximum and the maximum used as the minimum.

@param minimum The minimum inclusive value of the range. @param maximum The maximum inclusive value of the range. @see #Range(double, double) */ public Real_Range ( double minimum, double maximum ) {Range (minimum, maximum);} /** Constructs a single-valued Real_Range.

@param value The value of the range. @see #Value(double) */ public Real_Range ( double value ) {Value (value);} /** Constructs a Real_Range as a copy of another Real_Range.

@param range The Real_Range to be copied. If null a default Real_Range is constructed. @see #Range(Real_Range) */ public Real_Range ( Real_Range range ) {Range (range);} /** Constructs a Real_Range from an Integer_Range.

@param range The Integer_Range to be copied. If null a default Real_Range is constructed. @see #Range(Integer_Range) */ public Real_Range ( Integer_Range range ) {Range (range);} /** Clones this Real_Range.

@return A Real_Range constructed as a copy of this Real_Range. */ public Object clone () {return new Real_Range (this);} /** Sets the Range minimum and maximum.

Note: If the minimum is greater than the maximum then it is used as the maximum and the maximum used as the minimum.

@param minimum The minimum inclusive value of the range. @param maximum The maximum inclusive value of the range. @return This Real_Range. @see #Minimum(double) @see #Maximum(double) */ public Real_Range Range ( double minimum, double maximum ) { if (minimum <= maximum) { Minimum (minimum); Maximum (maximum); } else { Minimum (maximum); Maximum (minimum); } return this; } /** Sets the range from another Real_Range.

N.B.: The values of the source range are copied without checking.

@param range The Real_Range to be copied. If null nothing is done. @return This Real_Range. */ public Real_Range Range ( Real_Range range ) { if (range != null) { Minimum = range.Minimum; Maximum = range.Maximum; } return this; } /** Sets the range from an Integer_Range.

Long.MIN_VALUE and Long.MAX_VALUE from the Integer_Range are reset to Double.MIN_VALUE and Double.MAX_VALUE respectively.

@param range The Integer_Range limits. @return This Real_Range. @see #Minimum(double) @see #Maximum(double) */ public Real_Range Range ( Integer_Range range ) { if (range != null) { if (range.Minimum () == Long.MIN_VALUE) Minimum (Double.MIN_VALUE); else if (range.Minimum () == Long.MAX_VALUE) Minimum (Double.MAX_VALUE); else Minimum ((double)range.Minimum ()); if (range.Maximum () == Long.MIN_VALUE) Maximum (Double.MIN_VALUE); else if (range.Maximum () == Long.MAX_VALUE) Maximum (Double.MAX_VALUE); else Maximum ((double)range.Maximum ()); } return this; } /** Gets the minimum range value.

@return The minimum range value. */ public double Minimum () {return Minimum;} /** Sets the minimum range value.

If the new minimum value is greater than the current {@link #Maximum() maximum} value, the maximum is reset to the new minimum.

@param minimum The minimum range value. @return This Real_Range. */ public Real_Range Minimum ( double minimum ) { if ((Minimum = minimum) > Maximum) Maximum = minimum; return this; } /** Gets the maximum limit value.

@return The maximum limit value. */ public double Maximum () {return Maximum;} /** Sets the maximum range value.

If the new maximum rage value is less than the current {@link #Minimum() minimum} value, the minimum is reset to the new maximum.

@param maximum The maximum range value. @return This Real_Range. */ public Real_Range Maximum ( double maximum ) { if ((Maximum = maximum) < Minimum) Minimum = maximum; return this; } /** Tests if the Real_Range is open-ended.

@return true If either the {@link #Minimum() minimum} is {@link Double#MIN_VALUE} or the {@link #Maximum() maximum} is {@link Double#MAX_VALUE}. */ public boolean Is_Open_Ended () { return Maximum == Double.MAX_VALUE || Minimum == Double.MIN_VALUE; } /** Gets the distance between the range limits.

@return The difference between the maximum and minimum limit values, or Double.MAX_VALUE if the range is {@link #Is_Open_Ended() open-ended}. */ public double Distance () { if (Is_Open_Ended ()) return Double.MAX_VALUE; return Maximum - Minimum; } /** Tests if the range is for a single value.

For a single valued range the {@link #Minimum() minimum} and {@link #Maximum() maximum} values are identical.

@return true if the range only has a single value; false otherwise. */ public boolean Is_Single_Valued () {return Minimum == Maximum;} /** Sets the range to a single value.

Both the minimum and maximum are set to the value.

@param value The single value for the range. @return This Real_Range. @see #Minimum(double) @see #Maximum(double) */ public Real_Range Value ( double value ) { Minimum (value); return Maximum (value); } /** Compares two Real_Ranges for equality.

The two ranges are equal if, and only if, the argument is not null and the {@link #Minimum() minimum} and {@link #Maximum() maximum} values of the ranges are equal. */ public boolean equals ( Real_Range range ) { return range != null && range.Minimum == Minimum && range.Maximum == Maximum; } /** Gets a hash code for this Real_Range.

The result is the upper 16 bits of the hash code of the range minimum as a Double concatenated with the upper 16 bits of the hash code of the range maximum as a Double. That is, the hash code is the value of the expression:

(new Double (Minimum ()).hashCode () & 0xFFFF0000) | (new Double (Maximum ()).hashCode () >>> 16)

*/ public int hashCode () { return (new Double (Minimum).hashCode () & 0xFFFF0000) | (new Double (Maximum).hashCode () >>> 16); } /** Provides a String representation of the Real_Range.

The format of the Integer_Range representation is:

minimum-maximum

If the minimum and maximum are identical then only a single value is represented.

If the minimum is not {@link Double#MIN_VALUE} or the maximum is {@link Double#MAX_VALUE} then the minimum value is represented. Then a dash ('-') delimiter is provided regardless of whether the minimum is represented or not. If the maximum is not {@link Double#MAX_VALUE} then it is included in the representation after the '-' delimiter. Note that a range may be open-ended at its minimum or maximum, but in all cases at least one limit value will be represented with the minimum value being used if both both limits are open-ended.

@return A String representation of the Real_Range. */ public String toString () { StringBuffer range = new StringBuffer (); if (Minimum == Maximum) range.append (String.valueOf (Minimum)); else { if (Minimum != Double.MIN_VALUE || Maximum == Double.MAX_VALUE) range.append (String.valueOf (Minimum)); range.append ('-'); if (Maximum != Double.MAX_VALUE) range.append (String.valueOf (Maximum)); } return range.toString (); } } // End of Real_Range class. pirl-2.3.8/PIRL/Utilities/Styled_Writer.java0000644000175000017500000000500211742734660020537 0ustar mathieumathieu/* Styled_Writer PIRL CVS ID: Styled_Writer.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import javax.swing.text.AttributeSet; import java.io.IOException; /** A Styled_Writer is used to write styled text.

This interface is used to write text that may have various text style attributes - font face, size, color, etc. - associated with it.

@author Bradford Castalia - UA/PIRL @version 1.4 */ public interface Styled_Writer { /** Write styled text.

A style may be assembled using combinations of the numerous StyleConstants setXXX functions applied to a SimpleAttributeSet. For example:

SimpleAttributeSet bold_red_text = new SimpleAttributeSet ();
StyleConstants.setBold       (bold_red_text, true);
StyleConstants.setForeground (bold_red_text, Color.RED);
StyleConstants.setFontFamily (bold_red_text, "Monospaced");

assembles a style that will produce text in a bold, red, monospaced font.

@param text The text String to be written. @param style The AttributeSet to be applied to the text. This may be null if plain text is to be displayed. @return This Styled_Writer @see javax.swing.text.StyleConstants @see javax.swing.text.SimpleAttributeSet */ public Styled_Writer Write ( String text, AttributeSet style ) throws IOException; /** Write plain text.

This is the same as {@link #Write(String, AttributeSet) writing} text will a null style.

@param text The text String to be written. @return This Styled_Writer */ public Styled_Writer Write ( String text ) throws IOException; } pirl-2.3.8/PIRL/Utilities/package.html0000644000175000017500000000227111742734660017362 0ustar mathieumathieu The PIRL Utilities package contains various utility classes that provide general support - a.k.a. "helper" - functionality that has been found to be useful in different application contexts. pirl-2.3.8/PIRL/Utilities/Suspendable_Styled_Writer.java0000644000175000017500000002071511742734660023074 0ustar mathieumathieu/* Suspendable_Styled_Writer PIRL CVS ID: Suspendable_Styled_Writer.java,v 1.7 2012/04/16 06:18:24 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.io.Writer; import java.io.IOException; import javax.swing.text.AttributeSet; /** A Suspendable_Styled_Writer is a Writer that can have its output operations suspended.

All output methods are forwarded to the backing Writer. However, output may be suspended, in which case no output will occur until the suspension has been lifted.

The Styled_Writer interface is also implemented such that if the backing Writer implements Styled_Writer that interface is used, otherwise the usual Writer API is used instead in which case no style information will be forwarded.

@author Bradford Castalia - UA/PIRL @version 1.7 @see Writer @see Styled_Writer */ public class Suspendable_Styled_Writer extends Writer implements Styled_Writer { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utilities.Suspendable_Styled_Writer (1.7 2012/04/16 06:18:24)"; private Writer The_Writer; /** The suspension state of the Writer.

When Active is true all output methods will forward to the {@link #Writer() Writer} on which the object was constructed. */ public volatile boolean Active = true; private static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_WRITE = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a Suspendable_Styled_Writer.

The intial state of the Writer is determined by the {@link #Active flag}.

@param writer The Writer to use for all write operations. @throws IllegalArgumentException If the writer is null. */ public Suspendable_Styled_Writer ( Writer writer ) {this (writer, false);} /** Construct a Suspendable_Styled_Writer with suspension specified.

@param writer The Writer to use for all write operations. @param active The initial state of the Writer. If true writes will occure; if false writes are suspended. @throws IllegalArgumentException If the writer is null. */ public Suspendable_Styled_Writer ( Writer writer, boolean active ) { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">-< Suspendable_Styled_Writer: " + (active ? "" : "in") + "active " + writer); Active = active; if (writer == null) throw new IllegalArgumentException (ID + NL + "Null Writer specified."); The_Writer = writer; } private Suspendable_Styled_Writer () {} /*============================================================================== Accessors */ /** Get the Writer to which all Write methods are forwarded.

@return A Writer. */ public Writer Writer () {return The_Writer;} /** Test if output has been suspended.

@return true if output has been suspended; false otherwise. @see #Suspend(boolean) */ public boolean Suspended () {return ! Active;} /** Turn output suspension on or off.

@param suspend If true subsequent output will be suspended; if false output will occur. */ public void Suspend ( boolean suspend ) {Active = ! suspend;} /*============================================================================== Writer */ /** Write a portion of an array of characters.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@param characters The char array containing the characters to be written. If null or empty nothing is done. @param offset Array offset from which to start writing characters. @param amount The number of characters to write. @throws IOException If the {@link #Writer() Writer} throws an exception. */ public void write ( char[] characters, int offset, int amount ) throws IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Suspendable_Styled_Writer.write: " + Active + ' ' + offset + '/' + amount + " -" + NL + characters); if (Active) The_Writer.write (characters, offset, amount); } /** Write an array of characters.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@param characters The char array containing the characters to be written. @throws IOException If the {@link #Writer() Writer} throws an exception. */ public void write ( char[] characters ) throws IOException {if (Active) The_Writer.write (characters);} /** Write a character.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@param character The character to be written in the 16 low-order bits.. @throws IOException If the {@link #Writer() Writer} throws an exception. */ public void write ( int character ) throws IOException {if (Active) The_Writer.write (character);} /** Write a portion of a String.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@param string The String containing the characters to be written. @param offset String offset from which to start writing characters. @param amount The number of characters to write. @throws IOException If the {@link #Writer() Writer} throws an exception. */ public void write ( String string, int offset, int amount ) throws IOException {if (Active) The_Writer.write (string, offset, amount);} /** Write a String.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@param string The String containing the characters to be written. If null or empty nothing is done. @throws IOException If the {@link #Writer() Writer} throws an exception. */ public void write ( String string ) throws IOException {if (Active) The_Writer.write (string);} /** Flush the writer.

Nothing is done if the Writer is {@link #Suspended() suspended}.

@throws IOException If the {@link #Writer() Writer} throws an exception. */ public void flush () throws IOException {if (Active) The_Writer.flush ();} /** Close the writer.

N.B.: The {@link #Writer() Writer} will be closed regardless of whether writing has been {@link #Suspended() suspended} or not.

@throws IOException If the {@link #Writer() Writer} throws an exception. */ public void close () throws IOException {The_Writer.close ();} /*============================================================================== Styled_Writer */ /** Write styled text.

@param text The text String to be written. @param style The AttributeSet to be applied to the text. This may be null if plain text is to be displayed. @return This Suspendable_Styled_Writer @throws IOException If the {@link #Writer() Writer} throws an exception. @see Styled_Writer#Write(String, AttributeSet) */ public Styled_Writer Write ( String text, AttributeSet style ) throws IOException { if ((DEBUG & DEBUG_WRITE) != 0) System.out.println (">>> Suspendable_Styled_Writer.Write: " + NL +" Style: " + style + NL +" Styled_Writer: " + (The_Writer instanceof Styled_Writer) + NL + text); if (The_Writer instanceof Styled_Writer) ((Styled_Writer)The_Writer).Write (text, style); else The_Writer.write (text); return this; } /** Write plain text.

This is the same as {@link #Write(String, AttributeSet) writing} text with a null style.

@param text The text String to be written. @return This Suspendable_Styled_Writer @throws IOException If the {@link #Writer() Writer} throws an exception. */ public Styled_Writer Write ( String text ) throws IOException { if (The_Writer instanceof Styled_Writer) ((Styled_Writer)The_Writer).Write (text); else The_Writer.write (text); return this; } } pirl-2.3.8/PIRL/Utilities/Authentication.java0000644000175000017500000003101111742734657020723 0ustar mathieumathieu/* Authentication PIRL CVS ID: Authentication.java,v 1.7 2012/04/16 06:18:23 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.security.SecureRandom; import java.security.KeyPair; import java.security.PublicKey; import java.security.KeyPairGenerator; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** The Authentication class contains a set of functions used for public-private key pair authentication.

The typical uses for the Authentication functions is with a server that needs to authenticate a client connection. For example:

  1. Password - The client and the server obtain the same password.

    The password, a text string, need not be limited to a single, short word; a pass phrase is likely to be more secure. How the client and server obtain the password is application specific. A client is likely to obtain the password by means of an interactive dialog with the user. A server is likely to obtain the password from a permission protected file.

  2. Keys - The server initializes the public-private key pair.

    The key pair provides a public key that will be sent to the client and a private key that will be used to decrypt the encrypted password returned by the client. This may be done once at server startup if a single key pair is considered suffient for authenticating all clients. Alternatively each time a client connection occurs a key pair may be generated in which case each key pair must be associated with the corresponding client.

  3. Public_Key - The server generates an encoded public key.

    The public key from the key pair is serialized and encoded as a hexadecimal representation string. This is done to ensure that the public key can be sent by the server to the client even when binary data transport is not supported. As with the key pair, this may be done once for use with all clients or for each client connection.

  4. The server sends the encoded public key to the client.

    When a client connects to the server a handshake occurs during which authentication information is exchanged. The first step of the authentication handshake exchange is the server sending the public key to the client.

  5. Encoded_Password - The client encryptes and encodes its password using the public key.

    The public key received from the server is used to encrypt and encode the password. As with the public key, the encoded form of the encrypted password provides a hexidecimal respresentation string of encryption bytes which can be sent to the server even if binary data transport is not avaialable.

  6. The client sends the encoded password to the server.

    This is the second step of the client-server authentication handshake exchange.

  7. Authenticate - The server decodes and decryptes the encoded password using the private key.

    The server decodes and decrypts the encoded password received from the client using the private key of the key pair. If the resultant string matches the password that server knows then the client is successfully authenticated. Otherwise the client has failed to provide the required authentication information.

@author Andrew Davidson and Bradford Castalia - UA/PIRL @version 1.7 */ public class Authentication { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utilities.Authentication (1.7 2012/04/16 06:18:23)"; private static final int ENCRYPTION_KEY_BYTES = 512; private static final String ENCRYPTION_METHOD = "RSA", RANDOM_METHOD = "SHA1PRNG", RANDOM_PROVIDER = "SUN"; // No constructors for this class. private Authentication () {} /** Generate a public-private key pair.

A KeyPairGenerator is initialized with a SecureRandom seed and then used to generate a KeyPair.

@return A KeyPair to be used for asymmetric encryption. */ public static KeyPair Keys () throws NoSuchAlgorithmException, NoSuchProviderException { KeyPairGenerator key_pair_generator = KeyPairGenerator.getInstance (ENCRYPTION_METHOD); key_pair_generator.initialize (ENCRYPTION_KEY_BYTES, SecureRandom.getInstance (RANDOM_METHOD, RANDOM_PROVIDER)); return key_pair_generator.generateKeyPair (); } /** Create a string representation of a public key.

The {@link KeyPair#getPublic() public key} of the {@link #Keys() public-private key pair} is serialized as a byte array that is {@link #Encode_Hex(byte[]) encoded} as a string in hexadecimal notation.

An encoded public key is expected to be sent to a client for use in generating an {@link #Encoded_Password(String, String) encoded password}.

@param keys A KeyPair that contains a public key. @return A String representation of a public key. This will be null if the keys are null. */ public static String Public_Key ( KeyPair keys ) throws IOException { if (keys == null) return null; ByteArrayOutputStream byte_writer = new ByteArrayOutputStream (); ObjectOutputStream serializer = new ObjectOutputStream (byte_writer); serializer.writeObject (keys.getPublic ()); serializer.flush (); serializer.close (); return Encode_Hex (byte_writer.toByteArray ()); } /** Encrypte and encode a password using an encoded public key.

An {@link #Public_Key(KeyPair) encoded public key} is {@link #Decode_Hex(String) decoded} from its hexadecimal representation and then de-serialzed to a PublicKey object. This is used to encrypt the password string. The encrypted bytes are {@link #Encode_Hex(byte[]) encoded} into a hexadecimal representation string.

An encoded password provides secure authentication credentials for a client. It is expected to be sent to the server that provided the encoded public key for {@link #Authenticate(KeyPair, String, String) authentication}.

@param password The clear text String that is to be encrypted. @param public_key A hexadecimal String representation of a serialized PublicKey. @return A hexadecimal String representation of the encryped password. This will be null if the password is null, the public_key is null, the public_key could not be decoded or de-serialized, or the password could not be encrypted. Only a valid non-null String will be returned. */ public static String Encoded_Password ( String password, String public_key ) { if (password == null || public_key == null) return null; // De-serialize the public key. PublicKey key = null; try {key = (PublicKey) (new ObjectInputStream (new ByteArrayInputStream (Decode_Hex (public_key))) .readObject ());} catch (NumberFormatException e) {} catch (IOException e) {} catch (ClassNotFoundException e) {/* Shouldn't happen; PublicKey is a JFC */} if (key == null) return null; // Encrypt and encode the password. try { Cipher encrypter = Cipher.getInstance (key.getAlgorithm ()); encrypter.init (Cipher.ENCRYPT_MODE, key); return Encode_Hex (encrypter.doFinal (password.getBytes ())); } catch (InvalidKeyException e) {} catch (IllegalBlockSizeException e) {} catch (NoSuchAlgorithmException e) {} catch (BadPaddingException e) {} catch (NoSuchPaddingException e) {} return null; } /** Authenticate an encoded password.

The {@link #Encoded_Password(String, String) encoded} password string is {@link #Decode_Hex(String) decoded} and then decrypted using the {@link KeyPair#getPrivate() private key} of the {@link #Keys() key pair}. If the result matches all the same characters of the specified password string then authentication has succeeded.

N.B.: No exceptions are thrown; if any problem occurs the authentication fails.

@param keys The KeyPair used to decrypt the encoded password. If null false is returned. @param encoded_password An {@link #Encoded_Password(String, String) encoded} password string. If null false is returned. @param password The password string to be compared against the encoded password after it has been decoded and decrypted. If null false is returned. @return true if the decoded encoded password matches the password; false otherwise. N.B.: false will be returned if any of the arguments are null; any special rules, such as "anonymous" authenticaton when the password is null, are the responsibility of the application. */ public static boolean Authenticate ( KeyPair keys, String encoded_password, String password ) { if (keys == null || encoded_password == null || password == null) return false; // Compare the decoded/decrypted encoded password with the expected password. try { Cipher decryptor = Cipher.getInstance (keys.getPrivate ().getAlgorithm ()); decryptor.init (Cipher.DECRYPT_MODE, keys.getPrivate ()); return password.equals (new String (decryptor.doFinal (Decode_Hex (encoded_password)))); } catch (Exception e) {} return false; } /*============================================================================== Utilities */ /** Encode a byte array to a hexadecimal string representation.

Each byte in the array is represented by two characters that are the hexadecimal value of the byte ('0' padded if the value of the byte is less than or equal to 0xF). All hex characters for each byte of the array are concatenated in byte array order.

@param bytes An array of byte values. If null, null is returned. @return A String providing the hexadecimal representation of the byte array values. @see #Decode_Hex(String) */ public static String Encode_Hex ( byte[] bytes ) { if (bytes == null) return null; String string = ""; int value; for (int index = 0; index < bytes.length; index++) { value = (int)bytes[index] & 0xFF; if (value <= 0xF) string += "0"; string += Integer.toHexString (value).toUpperCase (); } return string; } /** Decode a hexadecimal string to its byte array equivalent.

Each pair of characters in the string are translated into the binary value they represent and stored in a byte array in the order in which they occur in the string.

@param string A String of hexadecimal character pairs. If null, null is returned. @return An array of bytes containing the values represented by each character pair in the string in the order they occur. @throws NumberFormatException If the length of the string is odd or any character in it does not represent a hexadecimal value (0-9 and a-f, case insensitive). @see #Encode_Hex(byte[]) */ public static byte[] Decode_Hex ( String string ) { if (string == null) return null; if ((string.length () % 2) != 0) throw new NumberFormatException ("Invalid odd length hex string to decode -\n" + string); byte[] bytes = new byte[string.length () >> 1]; char character; for (int index = 0, count = 0; count < bytes.length;) { try { bytes[count] = (byte)(Byte_Value (string.charAt (index++)) << 4); bytes[count++] += (byte)(Byte_Value (string.charAt (index++))); } catch (NumberFormatException exception) { throw new NumberFormatException (exception.getMessage () + string); } } return bytes; } private static int Byte_Value ( char character ) { int value; if (character >= '0' && character <= '9') return character - '0'; if (character >= 'a' && character <= 'f') return character - 'W'; if (character >= 'A' && character <= 'F') return character - '7'; throw new NumberFormatException ("Invalid character '" + character + "' in hex string to decode -\n"); } } pirl-2.3.8/PIRL/Utilities/Integer_Range.java0000644000175000017500000002264511742734660020464 0ustar mathieumathieu/* Integer_Range PIRL CVS ID: Integer_Range.java,v 1.14 2012/04/16 06:18:24 castalia Exp Copyright (C) 2007-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; /** An Integer_Range specifies a range of integer values.

The range of values is characterized by minimum and maximum values that identify the inclusive limits of the range. Single-valued and open-ended ranges are defined. An appropriate String representation is provided.

@author Bradford Castalia, UA/PIRL @version 1.14 */ public class Integer_Range implements Cloneable { public static final String ID = "PIRL.Utilities.Integer_Range (1.14 2012/04/16 06:18:24)"; /** The range minimum value. */ protected long Minimum = 0; /** The range maximum value. */ protected long Maximum = 0; /*============================================================================== Constructors */ /** Constructs a default Integer_Range.

The range values are initialized to zero. */ public Integer_Range () {} /** Constructs an Integer_Range from limiting values.

Note: If the minimum is greater than the maximum then it is used as the maximum and the maximum used as the minimum.

A range {@link #Is_Open_Ended() is open ended} if either the minimum is {@link Long#MIN_VALUE} or the maximum is {@link Long#MAX_VALUE}.

@param minimum The minimum inclusive limit value of the range. @param maximum The maximum inclusive limit value of the range. @see #Range(long, long) */ public Integer_Range ( long minimum, long maximum ) {Range (minimum, maximum);} /** Constructs a single-valued Integer_Range.

@param value The value of the range. @see #Value(long) */ public Integer_Range ( long value ) {Value (value);} /** Constructs an Integer_Range as a copy of another Integer_Range.

@param range The Integer_Range to be copied. If null a default Integer_Range is constructed. @see #Range(Integer_Range) */ public Integer_Range ( Integer_Range range ) {Range (range);} /** Constructs an Integer_Range from a Real_Range.

The values of the specified range are truncated and clipped to the Long.MIN_VALUE and Long.MAX_VALUE values.

@param range The Real_Range to be copied. If null a default Integer_Range is constructed. @see #Range(Real_Range) */ public Integer_Range ( Real_Range range ) {Range (range);} /** Clones this Integer_Range.

@return An Integer_Range constructed as a copy of this Integer_Range. */ public Object clone () {return new Integer_Range (this);} /*============================================================================== Accessors */ /** Sets the range minimum and maximum.

Note: If the minimum is greater than the maximum then it is used as the maximum and the maximum used as the minimum.

@param minimum The minimum inclusive value of the range. @param maximum The maximum inclusive value of the range. @return This Integer_Range. @see #Minimum(long) @see #Maximum(long) */ public Integer_Range Range ( long minimum, long maximum ) { if (minimum <= maximum) { Minimum (minimum); Maximum (maximum); } else { Minimum (maximum); Maximum (minimum); } return this; } /** Sets the range from another Integer_Range.

N.B.: The values of the source range are copied without checking.

@param range The Integer_Range to copy. If null, nothing is done. @return This Integer_Range. */ public Integer_Range Range ( Integer_Range range ) { if (range != null) { Minimum = range.Minimum; Maximum = range.Maximum; } return this; } /** Sets the range from a Real_Range.

The values of the Real_Range are truncated (not rounded) and clipped to the Long.MIN_VALUE and Long.MAX_VALUE values.

@param range The Real_Range to be copied. If null nothing is done. @return This Integer_Range. @see #Minimum(long) @see #Maximum(long) */ public Integer_Range Range ( Real_Range range ) { if (range != null) { if (range.Minimum () < (double)Long.MIN_VALUE) Minimum (Long.MIN_VALUE); else if (range.Minimum () > (double)Long.MAX_VALUE) Minimum (Long.MAX_VALUE); else Minimum ((long)range.Minimum ()); if (range.Maximum () < (double)Long.MIN_VALUE) Maximum (Long.MIN_VALUE); else if (range.Maximum () > (double)Long.MAX_VALUE) Maximum (Long.MAX_VALUE); else Maximum ((long)range.Maximum ()); } return this; } /** Gets the minimum range value.

@return The minimum range value. */ public long Minimum () {return Minimum;} /** Sets the minimum range value.

If the new minimum value is greater than the current {@link #Maximum() maximum} value, the maximum is reset to the new minimum.

@param minimum The minimum range value. @return This Integer_Range. */ public Integer_Range Minimum ( long minimum ) { if ((Minimum = minimum) > Maximum) Maximum = minimum; return this; } /** Gets the maximum range value.

@return The maximum range value. */ public long Maximum () {return Maximum;} /** Sets the maximum range value.

If the new maximum range value is less than the current {@link #Minimum() minimum} value, the minimum is reset to the new maximum.

@param maximum The maximum range value. @return This Integer_Range. */ public Integer_Range Maximum ( long maximum ) { if ((Maximum = maximum) < Minimum) Minimum = maximum; return this; } /** Gets the distance between the range values.

@return The difference between the maximum and minimum limit values, or Long.MAX_VALUE if the range is {@link #Is_Open_Ended() open-ended}. */ public long Distance () { if (Is_Open_Ended ()) return Long.MAX_VALUE; return Maximum - Minimum; } /** Tests if the range is open-ended.

@return true If either the {@link #Minimum() minimum} is {@link Long#MIN_VALUE} or the {@link #Maximum() maximum} is {@link Long#MAX_VALUE}. */ public boolean Is_Open_Ended () { return Maximum == Long.MAX_VALUE || Minimum == Long.MIN_VALUE; } /** Tests if the range is for a single value.

For a single valued range the {@link #Minimum() minimum} and {@link #Maximum() maximum} values are identical.

@return true if the range only has a single value; false otherwise. */ public boolean Is_Single_Valued () {return Minimum == Maximum;} /** Sets the range to a single value.

Both the minimum and maximum are set to the value.

@param value The single value for the range. @return This Integer_Range. @see #Minimum(long) @see #Maximum(long) */ public Integer_Range Value ( long value ) { Minimum (value); return Maximum (value); } /** Compares two Integer_Ranges for equality.

The two ranges are equal if, and only if, the argument is not null and the {@link #Minimum() minimum} and {@link #Maximum() maximum} values of the ranges are equal. */ public boolean equals ( Integer_Range range ) { return range != null && range.Minimum == Minimum && range.Maximum == Maximum; } /** Gets a hash code for this Integer_Range.

The result is the upper 16 bits of the hash code of the range minimum as a Long concatenated with the upper 16 bits of the hash code of the range maximum as a Long. That is, the hash code is the value of the expression:

(new Long (Minimum ()).hashCode () & 0xFFFF0000) | (new Long (Maximum ()).hashCode () >>> 16)

*/ public int hashCode () { return (new Long (Minimum).hashCode () & 0xFFFF0000) | (new Long (Maximum).hashCode () >>> 16); } /** Provides a String representation of the Integer_Range.

The format of the Integer_Range representation is:

minimum-maximum

If the minimum and maximum are identical then only a single value is represented.

If the minimum is not {@link Long#MIN_VALUE} or the maximum is {@link Long#MAX_VALUE} then the minimum value is represented. Then a dash ('-') delimiter is provided regardless of whether the minimum is represented or not. If the maximum is not {@link Long#MAX_VALUE} then it is included in the representation after the '-' delimiter. Note that a range may be open-ended at its minimum or maximum, but in all cases at least one limit value will be represented with the minimum value being used ifthe range is double open-ended.

@return A String representation of the Integer_Range. @see #Minimum() @see #Maximum() */ public String toString () { StringBuffer range = new StringBuffer (); if (Minimum == Maximum) range.append (String.valueOf (Minimum)); else { if (Minimum != Long.MIN_VALUE || Maximum == Long.MAX_VALUE) range.append (String.valueOf (Minimum)); range.append ('-'); if (Maximum != Long.MAX_VALUE) range.append (String.valueOf (Maximum)); } return range.toString (); } } // End of Integer_Range class. pirl-2.3.8/PIRL/Utilities/UNIX_Process.java0000644000175000017500000002113311742734660020223 0ustar mathieumathieu/* UNIX_Process PIRL CVS ID: UNIX_Process.java,v 1.4 2012/04/16 06:18:24 castalia Exp Copyright (C) 2009-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.lang.reflect.Field; import java.io.*; /** A UNIX_Process is an adapter for the UNIXProcess implementation of the abstract Process class that provides access to hidden information.

The API for the Java Process class, as of Java 1.6, has an unfortunate shortcoming: The user is unable to access the process identification (PID) of the executed process nor provide the timeout argument to the Object.wait method used by the waitFor method. Process is an abstract class that is implemented by a host OS dependent class - UNIXProcess for UNIX systems - which relies on JNI methods that make the necessary host process management system calls.

For the UNIXProcess class (as of Java 1.6) the PID value is held in the private "pid" variable. UNIX_Process provides an {@link #ID()} method to get the PID value.

The ability to provide the timeout argument is a trivial modification of the waitFor method. UNIX_Process provides a {@link #waitFor(long)} method that limits the wait for the process to exit to a timeout value.

An {@link #exited()} method provides direct access to the running/exited flag for the process.

All Process API methods are simply passed to the underlying Process.

@author Bradford Castalia - UA/PIRL @version 1.4 @see Process */ public class UNIX_Process extends Process { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Utililties.UNIX_Process (1.4 2012/04/16 06:18:24)"; private Process The_UNIXProcess; private static final String PID_FIELD_NAME = "pid", EXITED_FIELD_NAME = "hasExited"; private Field pid_Field = null, exited_Field = null; private static String NL = System.getProperty ("line.separator"); // Debug control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTORS = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Construct a UNIX_Process for a Process object on a UNIX system.

@param process The Process object obtained from a ProcessBuilder's start method or a Runtime exec method. @exception NoSuchFieldException If the process does not have one of the expected field variables. This will occur if the process is not implemented by a JFC UNIXProcess or the implementation has changed from what is expected. @exception SecurityException If a JVM SecurityManager prevents access to the private field values of the process object. @see ProcessBuilder @see Runtime */ public UNIX_Process ( Process process ) throws NoSuchFieldException, SecurityException { if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (">>> UNIX_Process"); The_UNIXProcess = process; Class process_Class = process.getClass (); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println (" Process class - " + process_Class.getName ()); pid_Field = get_Field (PID_FIELD_NAME, process_Class); exited_Field = get_Field (EXITED_FIELD_NAME, process_Class); if ((DEBUG & DEBUG_CONSTRUCTORS) != 0) System.out.println ("<<< UNIX_Process"); } private UNIX_Process () {} private Field get_Field ( String name, Class process_class ) throws NoSuchFieldException, SecurityException { Field field = null; try { field = process_class.getDeclaredField (name); field.setAccessible (true); } catch (NoSuchFieldException exception) { throw new NoSuchFieldException (ID + NL + "Unable to get the \"" + name + "\" field from the " + process_class.getName () + " class." + NL + exception.getMessage ()); } catch (SecurityException exception) { throw new SecurityException (ID + NL + "Unable to get the \"" + name + "\" field from the " + process_class.getName () + " class." + NL + exception.getMessage (), exception.getCause ()); } return field; } /*============================================================================== Accessors */ public OutputStream getOutputStream () {return The_UNIXProcess.getOutputStream ();} public InputStream getInputStream () {return The_UNIXProcess.getInputStream ();} public InputStream getErrorStream () {return The_UNIXProcess.getErrorStream ();} public int waitFor () throws InterruptedException {return The_UNIXProcess.waitFor ();} public int exitValue () {return The_UNIXProcess.exitValue ();} public void destroy () {The_UNIXProcess.destroy ();} /*============================================================================== Additional Accessors */ /** Get the Process ID that uniquely identifies the system process.

@return the integer ID used by the system to identify the process. */ public int ID () { try {return pid_Field.getInt (The_UNIXProcess);} catch (Exception e) {} return -1; } /** Wait for the process to exit up to a maximum timeout.

Causes the current thread to wait, if necessary, until the process represented by this Process object has terminated or the timeout period has expired. This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked. If the subprocess exits while the thread is blocked the exit status of the subprocess will be returned. If the timeout period expires while the thread is blocked an IOException will be thrown.

@param timeout The maximum amount of time, in milliseconds, to wait on subprocess exit. Wait forever if the value is <= 0 (the same as the {@link #waitFor()} method}. @return The exit value of the process. @exception InterruptedException If the current thread is {@link Thread#interrupt() interrupted} by another thread while it is waiting, then the wait is ended and an InterruptedException is thrown. @exception IOException If the timeout occurred before the process exit occurred. */ public synchronized int waitFor ( long timeout ) throws InterruptedException, IOException { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">>> UNIX_Process.waitFor: " + timeout); if (timeout < 0) timeout = 0; long wait_time = timeout, out_time = Long.MAX_VALUE; if (wait_time > 0) out_time = System.currentTimeMillis () + wait_time; int exit_status = -1; while (! exited ()) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Waiting " + wait_time + " ms for the process to exit or timeout..."); synchronized (The_UNIXProcess) {The_UNIXProcess.wait (wait_time);} if (! exited ()) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Process has not exited"); if ((wait_time = System.currentTimeMillis ()) >= out_time) throw new IOException (ID + NL + "Process timeout after " + timeout + " milliseconds."); if (timeout == 0) wait_time = 0; else wait_time = out_time - wait_time; } } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (" Process exited"); exit_status = exitValue (); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println ("<<< UNIX_Process.waitFor: " + exit_status); return exit_status; } /** Test if the process has exited.

The running/exited flag will only be set true after the forked process has exited.

This method tests the value of the UNIXProcess flag. The Process API may be used to determine if the process has exited via the {@link #exitValue()} method which will throw an IllegalThreadStateException if the process has not yet exited.

@return true if the process has exited; false if it is still running. */ public boolean exited () { try {return exited_Field.getBoolean (The_UNIXProcess);} catch (IllegalAccessException exception) {} return false; } } pirl-2.3.8/PIRL/Utilities/Streams.java0000644000175000017500000001005011742734660017354 0ustar mathieumathieu/* Streams PIRL CVS ID: Streams.java,v 1.11 2012/04/16 06:18:24 castalia Exp Copyright (C) 2004-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Utilities; import java.io.InputStream; import java.io.FileInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.FileImageInputStream; import PIRL.Image_Tools.HTTP_ImageInputStream; /** The Streams class provides a collection of methods for handling I/O streams.

@author Bradford Castalia, UA/PIRL @version 1.11 */ public class Streams { public static final String ID = "PIRL.Utilities.Streams (1.11 2012/04/16 06:18:24)"; //! No object constructor, just static functions. private Streams () {} /** Obtain an InputStream from a named source.

The source String is first treated as a URL specification. If this fails the source is treated as a pathname to a locally accessible file. If no file is found the source is treated as the name of a system resource, which can find a file inside a jar file when the application is being run from a jar.

@param source The name of a stream source. @return An InputStream, or null if no source file can be found. */ public static synchronized InputStream Get_Stream ( String source ) { if (source == null) return null; InputStream stream = null; try { // Treat the source as a URL. URL url = new URL (source); try {stream = url.openStream ();} catch (IOException exception) {/* Not accessable. */} } catch (MalformedURLException exception) {} if (stream == null) { // Treat the source as a filename. File file = new File (source); try { FileInputStream fstream = new FileInputStream (file); stream = (InputStream)fstream; } catch (FileNotFoundException exception) {} } if (stream == null) { // Treat the source as a system resource. URL url = ClassLoader.getSystemResource (source); if (url != null) { try {stream = url.openStream ();} catch (IOException exception) {/* Not accessable. */} } } return stream; } /** Obtain an ImageInputStream from a named source.

If a URL can be constructed from the source String an attempt is made to construct an HTTP_ImageInputStream from the URL. If the source is not a valid URL, the source is treated as a pathname to a locally accessible file and an attempt is made to construct a FileImageInputStream from the File.

@param source The name of a stream source. @return An ImageInputStream, or null if no source is accessible. */ public static synchronized ImageInputStream Get_Image_Stream ( String source ) { if (source == null) return null; ImageInputStream stream = null; try { // Treat the source as a URL. URL url = new URL (source); try {stream = new HTTP_ImageInputStream (url);} catch (Exception exception) {/* Not accessible */ return null;} } catch (MalformedURLException exception) {} if (stream == null) { // Treat the source as a filename. File file = new File (source); try {stream = new FileImageInputStream (file);} catch (Exception exception) {/* Not accessible */} } return stream; } } pirl-2.3.8/PIRL/Database/0000755000175000017500000000000012052546523014622 5ustar mathieumathieupirl-2.3.8/PIRL/Database/Connect_View.java0000644000175000017500000011041711742733570020061 0ustar mathieumathieu/* Connect_View PIRL CVS ID: Connect_View.java,v 1.11 2012/04/16 06:08:56 castalia Exp Copyright (C) 2005-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.PVL.*; import PIRL.Viewers.*; import PIRL.Configuration.*; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.util.Vector; import java.util.Iterator; import java.io.File; import java.io.IOException; /** Connect_View provides a dialog used to interactively obtain the information needed to make a connection to a Database.

A Connect_View dialog, which may be modal or non-modal, may contain a user supplied decorative icon on the left side of the panel. The right side of the panel contains a list of label fields used to gather the database connection parameters. Buttons across the bottom allow the user to Connect - accept the current connection parameters and close the dialog - Clear - clear all connection parameters - or Cancel - reject all parameters and close the dialog.

A user supplied Configuration provides an initial source of database connection parameters. If no Configuration is supplied, an attempt is made to read the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file}. If this fails the user is offered the opportunity to browse for a configuration file to read. If this is rejected an empty Configuration is used.

A specific set of Server configuration parameters is assembled from the connection parameter fields of the dialog. There are two categories of fields:

Required Fields

The fields for the required parameters will always be present. They are drawn from the user supplied Configuration:

Server
The database {@link Database#SERVER Server} parameters. This field is a drop-down list of each valid Server group; the user is notified when a Server list name does not have a corresponding group of parameters in the Configuration. Selecting an entry in the Server list results in the parameters of the Server group being used to set the values of all other fields, both required and additional, from each parameter that has a name matching one of the field names. The first Server name is used by default.

Note: When a parameter name in a selected Server group matches an additional field name, the parameter value is used to set the field value only when the field is currently empty or its value is not the user specified value.

Note: When the Server field is cleared the required fields are also cleared, but not any additional fields.

Type
The type of database server. This is used to select the appropriate {@link Data_Port} for managing the connection to the database server. A typical database type is MySQL. An entry in this field is required.
Host
The hostname of the computer system where the database server can be contacted. An entry in this field is required.
User
The username to supply when making a connection to the database server. Depending on the database connection requirements, this may be left empty.
Password
The password to supply when making a connection to the database server. The entry in this field will be masked. Depending on the database connection requirements, this may be left empty.

Additional Fields

An optional list containing the names of additional connection parameters to be obtained may be specified. Each field name may optionally be paired with an initial value for the field. All duplicate field names, including names that are the same (ignoring case) as any of the required field names, are removed leaving only the first unique name.

Buttons

Connect
Completion of user connection parameter entries. The field values are assembled into a server specific Configuration suitable for configuring a {@link Database} object. This Configuration will be named for the selected Server. N.B.: If the Server field is empty the Server name will be "unnamed". The Configuration will contain a {@link Database#SERVER Server} parameter with a value that is the selected Server name and a group with the same name containing a parameter for each field where the name of each parameter is the field name and its value is the contents of the field. The Connect_View dialog will be closed (setVisible (false)).

N.B.: If the Type or Host field is empty a notice dialog will be preseted, the server specific Configuration will not be assembled, and the Connect_View dialog will not close.

Reset
Reset the additional, non-required fields to their original values. If there are noe user specified additional fields this button will not be present.
Clear
All fields, both required and any additional, are cleared.
Cancel
The server configuration is set to null to indicate that the database connection was canceled and the Connect_View dialog is closed.

Connect_View is a reusable dialog. Once constructed it only needs to be setVisible to be activated. If it had been used previously the previous field values will remain. The current server configuration is renewed after each use unless the Type or Host fields are empty when Connect is pressed.

@author Bradford Castalia, UA/PIRL @version 1.11 @see Database @see Configuration */ public class Connect_View extends JDialog { /** Class name and version identification. */ public static final String ID = "PIRL.Database.Connect_View (1.11 2012/04/16 06:08:56)"; // Configuration. private Configuration User_Configuration, Server_Configuration = null; // Parameter names: private static final String PASSWORD_PARAMETER = "password", MASKED_VALUE = "*******"; // Icon displayed on the dialog box. private Icon Decorative_Icon = null; // Server configuration selection. private String Selected_Server = ""; private Vector Servers; private JComboBox Servers_List = new JComboBox (); // Required information fields. private JTextField Server_Type_Field = new JTextField (), Hostname_Field = new JTextField (), Username_Field = new JTextField (); private JPasswordField Password_Field = new JPasswordField (); // Additonal information fields. private Vector Additional_Field_Names, Additional_Field_Values = new Vector (), Additional_Fields = new Vector (); // Current Working Directory. private String CWD = System.getProperty ("user.dir"); // System new-line. private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_GET = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_UI = 1 << 3, DEBUG_HELPERS = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Database Connect_View dialog.

@param title The title String for the dialog window. @param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param additional_field_names A Vector of String values (or any Object that can be converted to a String) which are the names of additional parameters that are to be obtained beyond the required parameters. Instead of a String entry a Vector containing a pair of String values (or any Objects that can be converted to Strings) may be used in which the first value is the parameter name and the second value is its initial value. The Vector may be empty or null. @param owner The Frame with which the dialog is associated, which may be null. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( String title, Configuration configuration, Icon icon, Vector additional_field_names, Frame owner, boolean modal ) { super (owner, (title == null ? "Database Connection" : title), modal); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Connect_View"); Decorative_Icon = icon; if ((Additional_Field_Names = additional_field_names) == null) Additional_Field_Names = new Vector (); Initialize_Additional_Values (); // Setup the configuration. Configure (configuration); // Build the panels. getContentPane ().add (Panels (), BorderLayout.CENTER); // Initialize the field values. Server_Selected (); pack (); getRootPane ().setMinimumSize (getContentPane ().getSize ()); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Connect_View"); } /** Constructs a Database Connect_View dialog.

@param title The title String for the dialog window. @param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param additional_field_names A Vector of String values (or any Object that can be converted to a String) which are the names of additional parameters that are to be obtained beyond the required parameters. Instead of a String entry a Vector containing a pair of String values (or any Objects that can be converted to Strings) may be used in which the first value is the parameter name and the second value is its initial value. The Vector may be empty or null. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( String title, Configuration configuration, Icon icon, Vector additional_field_names, boolean modal ) {this (title, configuration, icon, additional_field_names, null, modal);} /** Constructs a Database Connect_View dialog.

@param title The title String for the dialog window. @param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( String title, Configuration configuration, Icon icon, boolean modal ) {this (title, configuration, icon, null, null, modal);} /** Constructs a Database Connect_View dialog.

@param title The title String for the dialog window. @param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( String title, Configuration configuration, boolean modal ) {this (title, configuration, null, null, null, modal);} /** Constructs a Database Connect_View dialog.

@param title The title String for the dialog window. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( String title, boolean modal ) {this (title, null, null, null, null, modal);} /** Constructs a Database Connect_View dialog.

@param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param modal If true, the dialog will be modal; if false, the dialog will not be modal. */ public Connect_View ( Configuration configuration, boolean modal ) {this (null, configuration, null, null, null, modal);} /** Constructs a default Connect_View. */ public Connect_View () {this (null, null, null, null, false);} /*------------------------------------------------------------------------------ PUBLIC INTERFACE */ /** Gets a Database Configuration.

@param title The title String for the dialog window. @param configuration The Configuration containing default Database access parameters. If null the {@link Database#DEFAULT_CONFIGURATION_FILENAME default configuration file} will be used. If this can found or read the user will be offered the opportunity to browser for a configuration file. If rejected, an empty Configuration will be supplied. @param icon The Icon to be displayed on the dialog. If null no Icon is displayed. @param additional_field_names A Vector of String values (or any Object that can be converted to a String) which are the names of additional parameters that are to be obtained beyond the required parameters. Instead of a String entry a Vector containing a pair of String values (or any Objects that can be converted to Strings) may be used in which the first value is the parameter name and the second value is its initial value. The Vector may be empty or null. @param parent The parent window that dialog is associated with, which may be null. @return A Configuration containing the parameters obtained, which should be suitable for configuring a Database to make the user defined connection. This will be null if the user Canelled the dialog. */ public static Configuration Get_Database_Configuration ( String title, Configuration configuration, Icon icon, Vector additional_field_names, JFrame parent ) { if ((DEBUG & DEBUG_GET) != 0) System.out.println (">>> Connect_View.Get_Database_Configuration"); Connect_View connect_view = new Connect_View (title, configuration, icon, additional_field_names, parent, true); connect_view.setVisible (true); if ((DEBUG & DEBUG_GET) != 0) System.out.println ("<<< Connect_View.Get_Database_Configuration"); return connect_view.Server_Configuration; } /** Gets the current database server connection parameters.

@return A Configuration containing the current database connection parameters. This may be null. */ public Configuration Database_Configuration () {return Server_Configuration;} /*============================================================================== Configuration The Configuration is expected to have a list of database server names and a Group of parameters for each named database server. */ private void Configure ( Configuration configuration ) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Connect_View.Configure"); if (configuration == null) { // Try the default configuration. try {configuration = new Configuration (Database.DEFAULT_CONFIGURATION_FILENAME);} catch (Exception exception) { if (Dialog_Box.Confirm ( "Unable to configure with the default file " + Database.DEFAULT_CONFIGURATION_FILENAME + NL + NL + exception.getMessage () + NL + NL + "Would you like to browse for a configuration file?")) // File selection dialog. configuration = Open_Configuration (); else // Use an empty configuration. configuration = new Configuration (); } } User_Configuration = configuration; boolean case_sensitive = configuration.Case_Sensitive (false); // Database Servers. Servers = configuration.Get (Configuration.Absolute_Pathname (Database.SERVER)); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Servers: " + Servers); // Validate all the server groups. int index = Servers.size (); while (--index >= 0) { try { if (Server_Group ((String)Servers.get (index)) == null) // Remove invalid server names. Servers.remove (index); } catch (Configuration_Exception e) { Servers.remove (index); } } if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Valid Servers: " + Servers); configuration.Case_Sensitive (case_sensitive); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Connect_View.Configure"); } private Configuration Open_Configuration () { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Connect_View.Open_Configuration"); Configuration configuration; JFileChooser file_chooser = new JFileChooser (CWD); file_chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); while (true) { if (file_chooser.showOpenDialog (rootPane) == JFileChooser.APPROVE_OPTION) { File file = file_chooser.getSelectedFile (); try { file = file.getCanonicalFile (); CWD = file.getParent (); try { configuration = new Configuration (file.getPath ()); break; } catch (Configuration_Exception except) { Dialog_Box.Error ("Unable to use the file -" + NL + file.getPath () + NL + NL + except.getMessage (), this); } } catch (IOException exception) { Dialog_Box.Error ("Unable to obtain the canonical pathname" + NL +"for the selected file -" + NL + file.getPath () + NL + NL + exception.getMessage (), this); } } else { configuration = new Configuration (); break; } } if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Connect_View.Open_Configuration"); return configuration; } /** Gets the Configuration for a specific Server.

@param server The name of the group of parameters in the current Configuration that is expected to contain database server access parameters. @return The Configuration for the server group of parameters, or null if the server group was not found. */ public Configuration Server_Configuration ( String server ) { if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (">>> Connect_View.Server_Configuration: " + server); Configuration server_configuration = null; if (server != null) { try { if ((server_configuration = Server_Group (server)) == null) Dialog_Box.Notice ("Database server group \"" + server + '"' + NL +"is not in the configuration.", this); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Database server group \"" + server + '"' + NL +"could not be assembled from the configuration." + NL + NL + ID + NL + exception.getMessage (), this); } } if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println ("<<< Connect_View.Server_Configuration:" + NL + ((server_configuration == null) ? "not found" : server_configuration.Description ())); return server_configuration; } private Configuration Server_Group ( String server ) throws Configuration_Exception { try { return User_Configuration.Group (Configuration.Absolute_Pathname (server)); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to assemble the \"" + server + "\" Configuration Group." + NL + NL + ID + NL + exception.getMessage (), this); throw exception; } } /*============================================================================== Panels */ private JPanel Panels () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> Connect_View.Panels"); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Icon or empty space. location.gridx = GridBagConstraints.RELATIVE; location.gridy = GridBagConstraints.RELATIVE; location.gridwidth = 1; location.gridheight = 5 + Additional_Field_Names.size (); location.weightx = 0.0; location.weighty = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.NORTH; location.insets = new Insets (10, 10, 10, 5); if (Decorative_Icon == null) // Place holder. panel.add (Box.createHorizontalGlue (), location); else // Decorative icon. panel.add (new JLabel (Decorative_Icon), location); // Servers. location.gridheight = 1; location.anchor = GridBagConstraints.EAST; location.insets = new Insets (10, 0, 5, 5); panel.add (new JLabel("Server:"), location); for (Iterator names = Servers.iterator (); names.hasNext (); Servers_List.addItem (names.next ())); Servers_List.addActionListener(new ActionListener () {public void actionPerformed (ActionEvent event) {Server_Selected ();}}); Servers_List.setEditable (true); Servers_List.setEnabled (true); Servers_List.setToolTipText ("Server configuration group"); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; location.insets = new Insets (10, 0, 5, 10); panel.add (Servers_List, location); // Type. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel("Type:"), location); Server_Type_Field.setToolTipText ("Type of database server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 10); panel.add (Server_Type_Field, location); // Hostname. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel("Host:"), location); Hostname_Field.setToolTipText ("Host name of the database server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 10); panel.add (Hostname_Field, location); // Username. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel("User:"), location); Username_Field.setToolTipText ("User name for the database server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 10); panel.add (Username_Field, location); // Password. location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 15, 5); panel.add (new JLabel("Password:"), location); Password_Field.setToolTipText ("Password for the database server"); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 15, 10); panel.add (Password_Field, location); // Additional user fields. Iterator names = Additional_Field_Names.iterator (), values = Additional_Field_Values.iterator (); while (names.hasNext ()) { location.gridx = 1; location.gridwidth = 1; location.weightx = 0.0; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 5, 5); panel.add (new JLabel((String)names.next () + ':'), location); JTextField field = new JTextField ((String)values.next ()); location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (0, 0, 5, 10); panel.add (field, location); Additional_Fields.add (field); } // Buttons panel: JPanel button_panel = new JPanel (new GridBagLayout ()); JButton button; location.gridx = GridBagConstraints.RELATIVE; location.gridwidth = 1; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.insets = new Insets (0, 0, 0, 0); // Connect. button = new JButton ("Connect"); button.setMnemonic ('C'); button.setToolTipText ("Proceed with the database connection"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Connect (true);}}); button.setDefaultCapable (true); getRootPane ().setDefaultButton (button); location.anchor = GridBagConstraints.WEST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); if (Additional_Field_Names.size () != 0) { // Reset. button = new JButton ("Reset"); button.setMnemonic ('R'); button.setToolTipText ("Reset non-required fields"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Reset ();}}); location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.CENTER; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); } // Clear. button = new JButton ("Clear"); button.setMnemonic ('l'); button.setToolTipText ("Clear all fields"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Clear (true);}}); location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.CENTER; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; button_panel.add (Box.createHorizontalGlue (), location); // Cancel. button = new JButton ("Cancel"); button.setMnemonic ('n'); button.setToolTipText ("Cancel the database connection"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Connect (false);}}); location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 0.0; location.fill = GridBagConstraints.NONE; location.anchor = GridBagConstraints.EAST; button_panel.add (button, location); location.weightx = 1.0; location.fill = GridBagConstraints.HORIZONTAL; location.insets = new Insets (10, 10, 10, 10); panel.add (button_panel, location); if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< Connect_View.Panels"); return panel; } /*============================================================================== Actions */ // Server Selected. private void Server_Selected () { String selected_server = (String)Servers_List.getSelectedItem (); if (selected_server == null) selected_server = ""; if ((DEBUG & DEBUG_UI) != 0) System.out.println (">>> Connect_View.Server_Selected: " + selected_server); Configuration server_configuration = null; if (selected_server.length () != 0) server_configuration = Server_Configuration (selected_server); if (server_configuration != null) { // Reset info fields from the configuration parameter values. Selected_Server = selected_server; if ((DEBUG & (DEBUG_UI | DEBUG_CONFIG)) != 0) System.out.println (" Connect_View.Server_Selected: Server Configuration -" + server_configuration.Description ()); Server_Type_Field.setText (server_configuration.Get_Specific_One (Database.TYPE)); Hostname_Field.setText (server_configuration.Get_Specific_One (Configuration.HOST)); Username_Field.setText (server_configuration.Get_Specific_One (Configuration.USER)); Password_Field.setText (server_configuration.Get_Specific_One (PASSWORD_PARAMETER)); for (int index = 0, total_additional_fields = Additional_Field_Names.size (); index < total_additional_fields; index++) { if ((DEBUG & (DEBUG_UI | DEBUG_CONFIG)) != 0) System.out.println (" Field " + index + ": " + Field_Name (index)); String value = server_configuration.Get_Specific_One (Field_Name (index)); if (value != null) { // Server config contains additional field name parameter. String field_value = Field_Value (index); if ((DEBUG & (DEBUG_UI | DEBUG_CONFIG)) != 0) System.out.println (" Config value - " + value + NL +" Field value - " + field_value + NL +" User value - " + Additional_Field_Value (index)); if (field_value.length () == 0 || ! field_value.equals (Additional_Field_Value (index))) { if ((DEBUG & (DEBUG_UI | DEBUG_CONFIG)) != 0) System.out.println (" New value - " + value); Field_Value (index, value); } } } int index = 0, total_items = Servers_List.getItemCount (); while (index < total_items) { if (selected_server.equals ((String)Servers_List.getItemAt (index))) break; ++index; } if (index == total_items) { // Add this unexpected server name to the list. Servers_List.addItem (selected_server); Servers_List.setSelectedIndex (index); } } else { if (selected_server.length () == 0) // Clear the server and required fields only. Clear (false); else // Restore the previously selected server. Servers_List.setSelectedItem (Selected_Server); } if ((DEBUG & DEBUG_UI) != 0) System.out.println ("<<< Connect_View.Server_Selected"); } private void Connect ( boolean connect ) { if (connect) { Configuration server_configuration = new Configuration (); server_configuration.Remove_All (); // Clear out. server_configuration.Name ((Selected_Server.length () == 0) ? "unnamed" : Selected_Server); String value; try { if ((value = Server_Type_Field.getText ()).length () == 0) { Dialog_Box.Notice ("The type of database server must be specified.", this); return; } server_configuration.Set (Database.TYPE, value); if ((value = Hostname_Field.getText ()).length () == 0) { Dialog_Box.Notice ("The hostname for the database server must be specified.", this); return; } server_configuration.Set (Configuration.HOST, value); server_configuration.Set (Configuration.USER, Username_Field.getText ()); server_configuration.Set (PASSWORD_PARAMETER, new String (Password_Field.getPassword ())); for (int index = 0, total_additional_fields = Additional_Field_Names.size (); index < total_additional_fields; index++) server_configuration.Set (Field_Name (index), Field_Value (index)); // Put the server configuration in a container configuration. Server_Configuration = new Configuration (); Server_Configuration.Remove_All (); // Clear out. Server_Configuration.Name (server_configuration.Name ()); Server_Configuration.Set (Database.SERVER, server_configuration.Name ()); Server_Configuration.Add (server_configuration); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to assemble the server configuration." + NL + NL + ID + NL + exception.getMessage (), this); Server_Configuration = null; return; } catch (PVL_Exception exception) { Dialog_Box.Error ("Unable to assemble the server configuration." + NL + NL + ID + NL + exception.getMessage (), this); Server_Configuration = null; return; } } setVisible (false); } /** Resets the non-interactive fields. */ private void Reset () { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">-< Connect_View.Reset"); for (int index = 0, total_additional_fields = Additional_Field_Names.size (); index < total_additional_fields; index++) Field_Value (index, Additional_Field_Value (index)); } /** Clears information fields.

@param clear_all If true all fields are cleared; otherwise only the required fields are cleared. */ private void Clear ( boolean clear_all ) { if ((DEBUG & DEBUG_UI) != 0) System.out.println (">-< Connect_View.Clear: clear_all = " + clear_all); Servers_List.getEditor ().setItem (Selected_Server = ""); Server_Type_Field.setText (null); Hostname_Field.setText (null); Username_Field.setText (null); Password_Field.setText (null); if (clear_all) { Iterator fields = Additional_Fields.iterator (); while (fields.hasNext ()) ((JTextField)fields.next ()).setText (null); } } /*============================================================================== Helpers */ private void Initialize_Additional_Values () { if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (">>> Connect_View.Initialize_Additional_Values: " + Additional_Field_Names); String name, value; int count = Additional_Field_Names.size (), index; // Generate the mirror Additional_Field_Values list. for (index = 0; index < count; index++) { value = ""; Object object = Additional_Field_Names.get (index); if (object instanceof Vector) { // Name, value pair. Additional_Field_Names.set (index, ((Vector)object).get (0)); object = ((Vector)object).get (1); if (object != null) value = object.toString (); } Additional_Field_Values.add (value); } while (--index >= 0) { name = Field_Name (index); if (name.equalsIgnoreCase (Database.TYPE) || name.equalsIgnoreCase (Configuration.HOST) || name.equalsIgnoreCase (Configuration.USER) || name.equalsIgnoreCase (PASSWORD_PARAMETER)) { Additional_Field_Names.remove (index); Additional_Field_Values.remove (index); } else { for (count = 0; count < index; count++) { if (name.equalsIgnoreCase (Field_Name (count))) { Additional_Field_Names.remove (index); Additional_Field_Values.remove (index); break; } } } } if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (" Additional_Field_Names - " + Additional_Field_Names + NL +" Additional_Field_Values - " + Additional_Field_Values + NL +"<<< Connect_View.Initialize_Additional_Values"); } private String Field_Name ( int index ) {return (String)Additional_Field_Names.get (index);} private String Field_Value ( int index ) {return ((JTextField)Additional_Fields.get (index)).getText ();} private void Field_Value ( int index, String value ) {((JTextField)Additional_Fields.get (index)).setText (value);} private String Additional_Field_Value ( int index ) {return (String)Additional_Field_Values.get (index);} /*============================================================================== Application test stub */ private static final Point DEFAULT_INITIAL_LOCATION = new Point (50, 25); public static void main (String[] arguments) { if (DEBUG != DEBUG_OFF) System.out.println ("*** " + ID); Vector additional_fields = new Vector (), field; field = new Vector (); field.add ("Catalog"); field.add ("catalog"); additional_fields.add (field); field = new Vector (); field.add ("Pipeline"); field.add ("pipeline"); additional_fields.add (field); try { Configuration configuration = null; if (arguments.length > 0) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Configuration: " + arguments[0]); configuration = new Configuration (arguments[0]); } if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" User Configuration -" + NL + ((configuration == null) ? "null" : configuration.Description ())); configuration = Get_Database_Configuration ("*** Connect_View ***", configuration, Icons.Load_Icon ("PIRL_Logo.gif"), additional_fields, null); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Database Configuration -" + NL + ((configuration == null) ? "null" : configuration.Description ())); } catch (Exception exception) { System.out.println (exception.getMessage ()); System.exit (1); } System.exit (0); } } // End of Connect_View class. pirl-2.3.8/PIRL/Database/Data_Table.java0000644000175000017500000010042511742733570017454 0ustar mathieumathieu/* Data_Table PIRL CVS ID: Data_Table.java,v 1.9 2012/04/16 06:08:56 castalia Exp Copyright (C) 2003-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import javax.swing.*; import javax.swing.border.*; import javax.swing.table.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import java.util.Vector; import java.util.TreeMap; import java.util.TreeSet; import java.util.Set; import java.util.Iterator; import java.util.EventObject; import java.lang.Float; /** Data_Table manages a JTable for Database_View.

@author Bradford Castalia, UA/PIRL @version 1.9 */ public class Data_Table extends JPanel { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Database.Data_Table (1.9 2012/04/16 06:08:56)"; private JScrollPane Table_View_Pane; // Data table elements. private JTable Table_View; private Data_Table_Model Table_Model; // Record numbers table elements. private JTable Record_Numbers; private AbstractTableModel Record_Numbers_Table_Model; private TableColumnModel Record_Numbers_Column_Model; private JLabel Record_Numbers_Label, Total_Records_Label; private static final int RECORD_NUMBERS_COLUMN_WIDTH = 70; // Colors. private Color Record_Numbers_Color = new Color ((float)0.85, (float)1.00, (float)0.85), Uneditable_Cell_Color = new Color ((float)1.00, (float)1.00, (float)1.00), Edited_Cell_Color = new Color ((float)1.00, (float)1.00, (float)0.00), Editable_Cell_Color = new Color ((float)1.00, (float)1.00, (float)0.90); // Work-around for bug in OS-X java. private static final Color BLACK = new Color ((float)0.00, (float)0.00, (float)0.00); private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_UI_LAYOUT = 1 << 1, DEBUG_LOCATIONS = 1 << 2, DEBUG_EDITOR = 1 << 3, DEBUG_RENDERER = 1 << 4, DEBUG_COLUMN_MODEL = 1 << 5, DEBUG_DATA = 1 << 6, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Data_Table ( Vector data_values, Vector field_values, boolean editable, Vector editable_fields ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Data_Table"); // Create the table's data model. Table_Model = new Data_Table_Model (data_values, field_values); // Set the editable fields. Table_Model.Editable (editable, editable_fields); // Build the table view. Create_GUI (); Set_Column_Sizes (Table_View); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Data_Table"); } public Data_Table ( Vector data_values, Vector field_values, boolean editable ) {this (data_values, field_values, editable, null);} public Data_Table ( Vector data_values, Vector field_values ) {this (data_values, field_values, true, null);} public Data_Table ( Vector table, boolean editable, Vector editable_fields ) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Data_Table"); Vector field_names = null; if (table != null && ! table.isEmpty ()) field_names = (Vector)table.remove (0); // Create the table's data model. Table_Model = new Data_Table_Model (table, field_names); // Set the editable fields. Table_Model.Editable (editable, editable_fields); // Build the table view. Create_GUI (); Set_Column_Sizes (Table_View); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Data_Table"); } public Data_Table ( Vector table, boolean editable ) {this (table, editable, null);} public Data_Table ( Vector table ) {this (table, true, null);} public Data_Table () {this (null, false, null);} /*============================================================================== Accessors */ public JTable Table_View () {return Table_View;} public Data_Table_Model Table_Model () {return Table_Model;} /*------------------------------------------------------------------------------ Data Table */ public void Data ( Vector data_values, Vector field_names, boolean editable, Vector editable_fields ) { if ((DEBUG & DEBUG_DATA) != 0) System.out.println (">>> Data_Table.Data"); // Apply the data to the table's data model. Table_Model.Data (data_values, field_names, editable, editable_fields); // Update the table pane. Set_Column_Sizes (Table_View); Show_Total_Records (); if ((DEBUG & DEBUG_DATA) != 0) System.out.println ("<<< Data_Table.Data"); } public void Data ( Vector data_values, Vector field_names, boolean editable ) {Data (data_values, field_names, editable, null);} public void Data ( Vector table, boolean editable ) { Vector field_names = null; if (table != null && ! table.isEmpty ()) field_names = (Vector)table.remove (0); Data (table, field_names, editable, null); } public void Data ( Vector table ) {Data (table, true);} public void Data () {Data (null, null, false, null);} public void setDataVector ( Vector data_values, Vector field_names ) {Data (data_values, field_names, true, null);} /*------------------------------------------------------------------------------ Cell Editing */ public void Editable ( boolean editable, int field_number ) {Table_Model.Editable (editable, field_number); repaint ();} public void Editable ( boolean editable, Vector field_numbers ) {Table_Model.Editable (editable, field_numbers); repaint ();} public void Editable ( boolean editable ) {Table_Model.Editable (editable); repaint ();} public Vector Edited_Cells () {return Table_Model.Edited_Cells ();} public void Clear_Edited_Cells () {Table_Model.Clear_Edited_Cells ();} /*------------------------------------------------------------------------------ Colors */ public Color Record_Numbers_Color () {return Record_Numbers_Color;} public Color Record_Numbers_Color ( Color color ) { Color previous_color = Record_Numbers_Color; Record_Numbers_Color = color; Record_Numbers_Label.setBackground (Record_Numbers_Color); Record_Numbers.setBackground (Record_Numbers_Color); Total_Records_Label.setBackground (Record_Numbers_Color); return previous_color; } public Color Uneditable_Cell_Color () {return Uneditable_Cell_Color;} public Color Uneditable_Cell_Color ( Color color ) { Color previous_color = Uneditable_Cell_Color; Uneditable_Cell_Color = color; return previous_color; } public Color Edited_Cell_Color () {return Edited_Cell_Color;} public Color Edited_Cell_Color ( Color color ) { Color previous_color = Edited_Cell_Color; Edited_Cell_Color = color; return previous_color; } public Color Editable_Cell_Color () {return Editable_Cell_Color;} public Color Editable_Cell_Color ( Color color ) { Color previous_color = Editable_Cell_Color; Editable_Cell_Color = color; return previous_color; } /*============================================================================== Table GUI */ private void Create_GUI () { if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (">>> Data_Table.Create_GUI"); GridBagLayout grid = new GridBagLayout (); GridBagConstraints location = new GridBagConstraints (); setLayout (grid); /*------------------------------------------------------------------------------ Record Numbers Table record numbering is provided by a JTable in a Viewport that is located in the row header area of the ScrollPane containing the main JTable for the table data. The JTable for row numbers uses a trivial AbstractTableModel that presents a single column of Integer values corresponding to the row number (+1 for row counts). These values are generated on demand by the getValueAt method. The number of rows is obtained from the Table_Model.getRowCount method. The main Table_Model JTable shares the same SelectionModel to keep selections in sync. */ if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Data_Table.Create_GUI - Constructing Record_Numbers_Table_Model"); Record_Numbers_Table_Model = new AbstractTableModel () { public String getColumnName (int column) {return "Record";} public int getRowCount () {return Table_Model.getRowCount ();} public int getColumnCount () {return 1;} public Object getValueAt (int row, int column) {return new Integer (row + 1);} public Class getColumnClass (int column) {return Integer.class;} }; if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Data_Table.Create_GUI - Constructing Record_Numbers_Column_Model"); Record_Numbers_Column_Model = new DefaultTableColumnModel () { public void addColumn (TableColumn column) { if ((DEBUG & DEBUG_COLUMN_MODEL) != 0) System.out.println ("--- Data_Table.Record_Numbers_Column_Model.addColumn: " + column.getModelIndex ()); DefaultTableCellRenderer renderer = new DefaultTableCellRenderer (); renderer.setHorizontalAlignment (SwingConstants.RIGHT); column.setCellRenderer (renderer); column.setMaxWidth (RECORD_NUMBERS_COLUMN_WIDTH); column.setResizable (false); super.addColumn (column); }}; if ((DEBUG & DEBUG_DATA) != 0) System.out.println (" Data_Table.Create_GUI: TableModelListener enabled."); // Coordinate the Record_Numbers_Table_Model with the Table_Model. Table_Model.addTableModelListener (new TableModelListener () { public void tableChanged (TableModelEvent event) { int operation = event.getType (); if ((DEBUG & DEBUG_DATA) != 0) { System.out.println ("--- Data_Table.TableModelListener event:"); switch (operation) { case TableModelEvent.DELETE: System.out.print ("DELETE"); break; case TableModelEvent.INSERT: System.out.print ("INSERT"); break; case TableModelEvent.UPDATE: System.out.print ("UPDATE"); break; default: System.out.print ("Type " + operation); } System.out.println (": rows " + event.getFirstRow () + " to " + event.getLastRow () +", column " + event.getColumn ()); } if (operation == TableModelEvent.DELETE) Record_Numbers_Table_Model.fireTableRowsDeleted (event.getFirstRow (), event.getLastRow ()); else if (operation == TableModelEvent.INSERT) Record_Numbers_Table_Model.fireTableRowsInserted (event.getFirstRow (), event.getLastRow ()); if (operation != TableModelEvent.UPDATE) Total_Records_Label.setText (String.valueOf (Table_View.getRowCount ())); }}); if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Data_Table.Create_GUI - Constructing Record_Numbers JTable"); Record_Numbers = new JTable (Record_Numbers_Table_Model, Record_Numbers_Column_Model); if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Data_Table.Create_GUI - createDefaultColumnsFromModel"); Record_Numbers.createDefaultColumnsFromModel (); Record_Numbers.setMaximumSize (new Dimension (RECORD_NUMBERS_COLUMN_WIDTH, 10000)); Record_Numbers.setBackground (Record_Numbers_Color); Record_Numbers.setColumnSelectionAllowed (false); Record_Numbers.setCellSelectionEnabled (false); Record_Numbers.setAutoResizeMode (JTable.AUTO_RESIZE_OFF); /*------------------------------------------------------------------------------ Data Table */ if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Data_Table.Create_GUI - Constructing Table_View JTable"); Table_View = new JTable (Table_Model); //Table_View.createDefaultColumnsFromModel (); Table_View.setAutoResizeMode (JTable.AUTO_RESIZE_ALL_COLUMNS); // Override the default cell renderer and editor. Table_View.setDefaultRenderer (Object.class, new Data_Cell_Renderer ()); Table_View.setDefaultEditor (Object.class, new Data_Cell_Editor ()); // Share the SelectionModel to keep selections in sync. Table_View.setSelectionModel (Record_Numbers.getSelectionModel ()); Table_View.setCellSelectionEnabled (true); Table_View.getTableHeader ().setBorder (BorderFactory.createMatteBorder (0, 1, 0, 0, BLACK)); /*------------------------------------------------------------------------------ Scroll pane */ Table_View_Pane = new JScrollPane (Table_View); Table_View_Pane.setViewportBorder (BorderFactory.createMatteBorder (0, 1, 0, 0, BLACK)); // Put the record numbers table in a viewport in the scroll pane row header. JViewport viewport = new JViewport (); viewport.setView (Record_Numbers); viewport.setPreferredSize (Record_Numbers.getMaximumSize ()); Table_View_Pane.setRowHeader (viewport); // Put a record numbers table header in the scroll pane upper-left corner. Record_Numbers_Label = new JLabel ("Record", SwingConstants.CENTER); Record_Numbers_Label.setBorder (BorderFactory.createMatteBorder (0, 0, 2, 0, BLACK)); Record_Numbers_Label.setMinimumSize (new Dimension (RECORD_NUMBERS_COLUMN_WIDTH + 3, Table_View.getTableHeader ().getHeight ())); Record_Numbers_Label.setBackground (Record_Numbers_Color); Record_Numbers_Label.setOpaque (true); Table_View_Pane.setCorner (JScrollPane.UPPER_LEFT_CORNER, Record_Numbers_Label); // Put a total records label in the scroll pane lower-left corner. Total_Records_Label = new JLabel (String.valueOf (Table_View.getRowCount ()), SwingConstants.RIGHT); Total_Records_Label.setBorder (BorderFactory.createMatteBorder (2, 0,0, 0, BLACK)); Total_Records_Label.setMinimumSize (new Dimension (RECORD_NUMBERS_COLUMN_WIDTH + 3, Record_Numbers.getRowHeight ())); Total_Records_Label.setBackground (Record_Numbers_Color); Total_Records_Label.setOpaque (true); Table_View_Pane.setCorner (JScrollPane.LOWER_LEFT_CORNER, Total_Records_Label); // Keep the horizonal scroll bar aways visible. Table_View_Pane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); // Put the scroll pane into the enclosing pane. location.insets = new Insets (0, 10, 0, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; add (Table_View_Pane, location); if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println ("<<< Data_Table.Create_GUI"); } /* Sets the width of each table column to accommodate its field name, and turns off column auto resizing if the table is wider than its pane. */ private void Set_Column_Sizes ( JTable table ) { if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (">>> Data_Table.Set_Column_Sizes: " + table.getColumnCount () + " columns"); TableCellRenderer header = Table_View.getTableHeader().getDefaultRenderer(); TableColumn column; int column_number, column_count = Table_View.getColumnCount (), column_width, table_width = 0; for (column_number = 0; column_number < column_count; column_number++) { column = Table_View.getColumnModel ().getColumn (column_number); column_width = header.getTableCellRendererComponent (null, column.getHeaderValue (), false, false, 0, 0) .getPreferredSize ().width + 8; column.setPreferredWidth (column_width); column.setMinWidth (column_width); table_width += column_width; if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" " + column_number + ": " + column_width); } table_width += Record_Numbers.getWidth (); if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println (" Table_View_Pane width = " + Table_View_Pane.getWidth () + NL +" table_width = " + table_width); Table_View.setAutoResizeMode ((table_width > Table_View_Pane.getWidth ()) ? JTable.AUTO_RESIZE_OFF : JTable.AUTO_RESIZE_ALL_COLUMNS); Table_View.doLayout (); if ((DEBUG & DEBUG_UI_LAYOUT) != 0) System.out.println ("<<< Data_Table.Set_Column_Sizes"); } private void Show_Total_Records () { Total_Records_Label.setText (String.valueOf (Table_View.getRowCount ())); Record_Numbers_Table_Model.fireTableDataChanged (); } /*============================================================================== Cell Renderer */ class Data_Cell_Renderer extends DefaultTableCellRenderer { public Component getTableCellRendererComponent ( JTable table, Object value, boolean is_selected, boolean has_focus, int row, int column ) { if ((DEBUG & DEBUG_RENDERER) != 0) System.out.println ("--- Data_Table.Data_Cell_Renderer.getTableCellRendererComponent:" + NL + " " + row + ',' + column + ": selected = " + is_selected + ", has focus = " + has_focus + " - " + value); Component component = super.getTableCellRendererComponent (table, value, is_selected, has_focus, row, column); if (value instanceof String) { try { Float.valueOf ((String)value); setHorizontalAlignment (SwingConstants.RIGHT); } catch (NumberFormatException exception) {setHorizontalAlignment (SwingConstants.LEFT);} } else setHorizontalAlignment (SwingConstants.LEFT); if (Table_Model.is_Cell_Edited (row, column)) component.setBackground (Edited_Cell_Color); else if (Table_Model.isCellEditable (row, column)) component.setBackground (Editable_Cell_Color); else component.setBackground (Uneditable_Cell_Color); return component; } } // End of Data_Cell_Renderer class. /*============================================================================== Cell Editor */ class Data_Cell_Editor extends DefaultCellEditor { private int Edited_Row = -1, Edited_Column = -1; Data_Cell_Editor () { super (new JTextField ()); super.setClickCountToStart (2); if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println ("--- Data_Table.Data_Cell_Editor:"); ((JTextField)super.getComponent ()).addKeyListener (new KeyListener () { public void keyPressed (KeyEvent key_event) {} public void keyTyped (KeyEvent key_event) { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println ("--- Data_Table.Data_Cell_Editor: keyTyped:"); if (Edited_Row < 0) { Edited_Row = Table_View.getEditingRow (); Edited_Column = Table_View.getEditingColumn (); if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println (" Edited_Row " + Edited_Row + ", Edited_Column " + Edited_Column); } } public void keyReleased (KeyEvent key_event) {} }); } public Component getTableCellEditorComponent ( JTable table, Object value, boolean is_selected, int row, int column ) { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println ("--- Data_Table.Data_Cell_Editor.getTableCellEditorComponent:" + NL + " " + row + ',' + column + ": selected = " + is_selected + " - " + value); Edited_Row = -1; return super.getTableCellEditorComponent (table, value, is_selected, row, column); } public boolean stopCellEditing () { if ((DEBUG & DEBUG_EDITOR) != 0) System.out.println ("--- Data_Table.Data_Cell_Editor.stopCellEditing: " + Table_View.getSelectedRow () + "," + Table_View.getSelectedColumn ()); if (Edited_Row >= 0) { //Table_Model.Edited_Location (Edited_Row, Edited_Column); if ((DEBUG & DEBUG_EDITOR) != 0) Table_Model.Edited_Cells (); } return super.stopCellEditing (); } } // End of Data_Cell_Editor class. /*============================================================================== Testing stub */ public static void main (String[] arguments) { int rows = 30, columns = 15; if (arguments.length > 0) { try { rows = Integer.parseInt (arguments[0]); if (arguments.length > 1) columns = Integer.parseInt (arguments[1]); } catch (NumberFormatException exception) {rows = -1;} if (rows < 0 || columns < 0) { System.out.println (NL +"Usage: Data_Table [ []"); System.exit (1); } } // Main panel. JPanel panel = new JPanel (); GridBagLayout grid = new GridBagLayout (); GridBagConstraints location = new GridBagConstraints (); panel.setLayout (grid); // Select fields that can be edited. Vector editable_fields = new Vector (); editable_fields.add (new Integer (0)); editable_fields.add (new Integer (2)); editable_fields.add (new Integer (4)); // The Data_Table. final Data_Table Table = new Data_Table (table_data (rows, columns), true, editable_fields); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panel.add (Table, location); // Report a listing of edited cells. JButton Report = new JButton ("Report"); Report.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { Vector edited_cells = Table.Edited_Cells (); System.out.println (edited_cells.size () + " cells edited"); Iterator cells = edited_cells.iterator (); while (cells.hasNext ()) { Cell cell = (Cell)cells.next (); System.out.println (" " + cell.row + "," + cell.column + ": " + cell.new_value); } }}); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; panel.add (Report, location); // Reset all edited cells. JButton Reset = new JButton ("Reset"); Reset.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Table.Clear_Edited_Cells ();}}); panel.add (Reset, location); // Turn editing off and on. final JButton Editable = new JButton ("Edit None"); Editable.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { String label = Editable.getText (); if (label.equals ("Edit None")) { Table.Editable (false); Editable.setText ("Edit All"); } else { Table.Editable (true); Editable.setText ("Edit None"); } }}); panel.add (Editable, location); // Reload new rows and columns. JLabel label = new JLabel (" Rows:"); location.anchor = GridBagConstraints.EAST; panel.add (label, location); final JTextField Rows = new JTextField (String.valueOf (rows), 5); location.anchor = GridBagConstraints.WEST; panel.add (Rows, location); label = new JLabel (" Columns:"); location.anchor = GridBagConstraints.EAST; panel.add (label, location); final JTextField Columns = new JTextField (String.valueOf (columns), 5); location.anchor = GridBagConstraints.WEST; panel.add (Columns, location); Rows.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { try { int rows = Integer.parseInt (Rows.getText ()), columns = Integer.parseInt (Columns.getText ()); Table.Data (table_data (rows, columns)); } catch (NumberFormatException exception) {} }}); Columns.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) { try { int rows = Integer.parseInt (Rows.getText ()), columns = Integer.parseInt (Columns.getText ()); Table.Data (table_data (rows, columns)); } catch (NumberFormatException exception) {} }}); final JFrame frame = new JFrame ("Data_Table"); frame.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent e) {System.exit (0);}}); frame.setContentPane (panel); frame.pack (); frame.setVisible (true); } private static Vector table_data ( int rows, int columns ) { // Assemble a table of data. Vector table = new Vector (), record; int row, column; for (row = 0; row < rows + 1; row++) { record = new Vector (); for (column = 0; column < columns; column++) { if (row == 0) record.add ("Field " + column); else if (row == 1) record.add ("cell " + String.valueOf (row - 1) + '.' + String.valueOf (column)); else record.add (String.valueOf (row - 1) + '.' + String.valueOf (column)); } table.add (record); } return table; } } // End of Data_Table class. /* ***************************************************************************** Data Table Model */ class Data_Table_Model extends DefaultTableModel { public static final String ID = "PIRL.Database.Data_Table_Model (1.9 2012/04/16 06:08:56)"; private static Vector EMPTY_FIELD = new Vector (1); static {EMPTY_FIELD.add ("Empty");} private static Vector EMPTY_TABLE = new Vector (1); // The Set of column numbers (Integer) that can be edited. private TreeSet Editable_Fields = new TreeSet (); /** A map of data table cells that have been edited.

The Edited_Cells_Map maps the number of each row (as an Integer) that contains an edited cell to a TreeMap of each column number (as an Integer) in the row that was edited mapped to its old (unedited) value. The use of TreeMaps ensures that the Edited_Cells_Map will be ordered by row and by column within rows. */ private TreeMap Edited_Cells_Map = new TreeMap (); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_DATA_VALUE = 1 << 0, DEBUG_DATA_CHANGE = 1 << 1, DEBUG_EDITABLE = 1 << 2, DEBUG_METADATA = 1 << 3, DEBUG_LOCATIONS = 1 << 4, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Data_Table_Model () {super (EMPTY_TABLE, EMPTY_FIELD);} public Data_Table_Model ( Vector data_values, Vector field_names ) { super (((data_values == null || field_names == null || field_names.isEmpty ()) ? EMPTY_TABLE : data_values), ((data_values == null || field_names == null || field_names.isEmpty ()) ? EMPTY_FIELD : field_names)); } /*============================================================================== AbstractTableModel interface */ public void setValueAt ( Object value, int row, int column ) throws ArrayIndexOutOfBoundsException { if ((DEBUG & DEBUG_DATA_VALUE) != 0) System.out.println ("--- Data_Table_Model.setValueAt: " + row + "," + column + ", " + value + '(' + getValueAt (row, column) + ')'); if (! getValueAt (row, column).equals (value)) { Edited_Cell (row, column, (String)getValueAt (row, column)); super.setValueAt (value, row, column); } } public boolean isCellEditable ( int row, int column ) { if ((DEBUG & DEBUG_DATA_VALUE) != 0) System.out.print ("--- Data_Table_Model.isCellEditable (" + row + ", " + column + "): "); if ((DEBUG & DEBUG_DATA_VALUE) != 0) System.out.println (Editable_Fields.contains (new Integer (column))); return Editable_Fields.contains (new Integer (column)); } /*============================================================================== Editing */ /*------------------------------------------------------------------------------ Editable Cells */ private void Editable ( boolean editable, int field_number, boolean signal_change ) { if ((DEBUG & DEBUG_EDITABLE) != 0) System.out.println ("--- Data_Table_Model.Editable: " + editable + " - " + field_number); if (editable) { if (field_number < 0) { int total_fields = getColumnCount (); field_number = 0; while (field_number < total_fields) Editable_Fields.add (new Integer (field_number++)); } else Editable_Fields.add (new Integer (field_number)); } else { if (field_number < 0) Editable_Fields.clear (); else Editable_Fields.remove (new Integer (field_number)); } if (signal_change) fireTableDataChanged (); } public void Editable ( boolean editable, int field_number ) {Editable (editable, field_number, true);} private void Editable ( boolean editable, Vector field_numbers, boolean signal_change ) { if (field_numbers == null) Editable (editable, -1, true); // Apply to all fields. else { Iterator fields = field_numbers.iterator (); while (fields.hasNext ()) Editable (editable, ((Integer)fields.next ()).intValue (), // Signal data change on the last field number. (fields.hasNext () ? false : signal_change)); } } public void Editable ( boolean editable, Vector field_numbers ) {Editable (editable, field_numbers, true);} public void Editable ( boolean editable ) {Editable (editable, -1, true);} /*------------------------------------------------------------------------------ Edited Cells */ /** Gets a list of cells that have been edited.

The contents of the Edited_Cells_Map is converted to a Vector of {@link Cell Cell} objects, one for each cell that was edited. The Cell objects will be ordered in the Vector by row and column within each row.

@return A Vector of Cell objects. */ public Vector Edited_Cells () { Vector edited_cells = new Vector (); if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.println ("--- Data_Table_Model.Edited_Cells:"); Set row_list = Edited_Cells_Map.keySet (); Iterator rows = row_list.iterator (); while (rows.hasNext ()) { Integer row = (Integer)rows.next (); TreeMap column_map = (TreeMap)Edited_Cells_Map.get (row); Set column_list = column_map.keySet (); Iterator columns = column_list.iterator (); while (columns.hasNext ()) { Integer column = (Integer)columns.next (); edited_cells.add (new Cell (row, column, (String)getValueAt (row.intValue (), column.intValue ()), (String)column_map.get (column))); if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.println (" " + ((Cell)edited_cells.lastElement ()).row + "," + ((Cell)edited_cells.lastElement ()).column + ": " + ((Cell)edited_cells.lastElement ()).new_value + "(" + ((Cell)edited_cells.lastElement ()).old_value + ")"); } } return edited_cells; } public void Clear_Edited_Cells () { if (Edited_Cells_Map.size () != 0) { Edited_Cells_Map.clear (); fireTableDataChanged (); } } public boolean is_Cell_Edited ( int row, int column ) { TreeMap columns; if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.println ("--- Data_Table_Model.is_Cell_Edited (" + row + ", " + column + "): " + ((columns = (TreeMap)Edited_Cells_Map.get (new Integer (row))) != null && columns.containsKey (new Integer (column)))); return ((columns = (TreeMap)Edited_Cells_Map.get (new Integer (row))) != null && columns.containsKey (new Integer (column))); } private void Edited_Cell ( int row, int column, String value ) { if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.print ("--- Data_Table_Model.Edited_Cell: " + row + "," + column + ": " + value); Integer row_number = new Integer (row), column_number = new Integer (column); TreeMap column_numbers; if ((column_numbers = (TreeMap)Edited_Cells_Map.get (row_number)) != null) { if (column_numbers.containsKey (column_number)) { if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.println ("redundant"); return; } } else { column_numbers = new TreeMap (); Edited_Cells_Map.put (row_number, column_numbers); } column_numbers.put (column_number, value); if ((DEBUG & DEBUG_LOCATIONS) != 0) System.out.println ("added"); } /*============================================================================== Data */ public void Data ( Vector data_values, Vector field_names, boolean editable, Vector editable_fields ) { if ((DEBUG & DEBUG_DATA_CHANGE) != 0) System.out.println (">>> Data_Table_Model.Data"); // Apply the data values and field names. if (data_values == null || field_names == null || field_names.isEmpty ()) { data_values = EMPTY_TABLE; field_names = EMPTY_FIELD; } if ((DEBUG & DEBUG_DATA_CHANGE) != 0) System.out.println (">>> Data_Table_Model.Data: " + data_values.size () + " rows, " + field_names.size () + " columns"); // Apply the editable fields (don't update the table, yet). if (editable_fields == null) { int total_fields = field_names.size (), field_number = 0; while (field_number < total_fields) Editable (editable, field_number++, false); } else Editable (editable, editable_fields, false); // Apply the new data. super.setDataVector (data_values, field_names); if ((DEBUG & DEBUG_DATA_CHANGE) != 0) System.out.println ("<<< Data_Table_Model.Data: " + getColumnCount () + " columns"); } } // End of Data_Table_Model class. /* ***************************************************************************** Edited Cells */ /** Information about an edited cell.

Objects of this class are returned, in a Vector, to report to the user information on the cells that have been edited.

@see Data_Table_Model#Edited_Cells() */ class Cell { int row; int column; String new_value; String old_value; public Cell ( int row, int column, String new_value, String old_value ) { this.row = row; this.column = column; this.new_value = new_value; this.old_value = old_value; } public Cell ( Integer row, Integer column, String new_value, String old_value ) { this.row = row.intValue (); this.column = column.intValue (); this.new_value = new_value; this.old_value = old_value; } private Cell () {} } // End of Cell class. pirl-2.3.8/PIRL/Database/Data_View0000755000175000017500000000561311652360267016424 0ustar mathieumathieu#!/usr/bin/perl # # Data_View # # A wrapper for the Data_View Java application. # # The PIRL_JAVA_HOME enviroment variable, if it is set, will be # prepended to the java runtime classpath. The value of PIRL_JAVA_HOME # may be a directory pathname where the PIRL subdirectory and all its # class files is located; or it may be the pathname to a jar file - # e.g. PIRL.jar - containing the class files for the PIRL Java # Packages. # # N.B.: If the PIRL_JAVA_HOME enviroment variable is not set but the # /opt/java/PIRL pathname exists, then /opt/java will be used for the # value of PIRL_JAVA_HOME. # # If the CLASSPATH environment variable is set its value is appended to # the java runtime classpath. # # The location of the following third party packages are expected to be # in the java runtime classpath: # # MySQL_JDBC - http://dev.mysql.com/downloads/connector/j/ # # The pathname to the MySQL JDBC driver jar file or the root directory # where its class files are located. Either the MySQL JDBC Driver or # the PostgreSQL JDBC Driver is required to provide Database support. # Default: $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # # PostgreSQL_JDBC - http://jdbc.postgresql.org/ # # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. Either the PostgreSQL # JDBC Driver or the MySQL JDBC Driver is required to provide Database # support. # Default: $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # # JCM - http://math.hws.edu/javamath # # The pathname to the Java Components for Mathematics jar file or the # root directory where its class files are located. This package is a # required dependency. # Default: $PIRL_JAVA_HOME/jcm/jcm_data.jar # # Note: On Apple OS X (Darwin) systems the Java Virtual Machine will # automatically include jar files in the ~/Library/Java/Extensions and # /Library/Java/Extensions directories, if they exist, in the java # runtime classpath. # # # CVS ID: Data_View,v 1.3 2011/10/27 22:52:39 castalia Exp $CLASSPATH = $ENV{"CLASSPATH"}; $PIRL_JAVA_HOME = $ENV{"PIRL_JAVA_HOME"}; $PIRL_JAVA_HOME = "/opt/java" if (! $PIRL_JAVA_HOME && -e "/opt/java/PIRL"); Add_to_CLASSPATH ($PIRL_JAVA_HOME); $MySQL_JDBC = "$PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar" unless $MySQL_JDBC = $ENV{"MySQL_JDBC"}; Add_to_CLASSPATH ($MySQL_JDBC); $PostgreSQL_JDBC = "$PIRL_JAVA_HOME/PostgreSQL/postgresql.jar" unless $PostgreSQL_JDBC = $ENV{"PostgreSQL_JDBC"}; Add_to_CLASSPATH ($PostgreSQL_JDBC); $JCM = "$PIRL_JAVA_HOME/jcm/jcm_data.jar" unless $JCM = $ENV{"JCM"}; Add_to_CLASSPATH ($JCM); @Arguments = (java); push @Arguments, "-cp", $CLASSPATH if $CLASSPATH; push @Arguments, "PIRL.Database.Data_View", @ARGV; exec @Arguments; sub Add_to_CLASSPATH { my ($pathname) = @_; if (-e "$pathname") { if ($CLASSPATH) { $CLASSPATH = "$CLASSPATH:$pathname" unless "$pathname" =~ /"$pathname"/; } else { $CLASSPATH = $pathname; } } } pirl-2.3.8/PIRL/Database/Data_Port.java0000644000175000017500000007540711742733570017364 0ustar mathieumathieu/* Data_Port PIRL CVS ID: Data_Port.java,v 2.3 2012/04/16 06:08:56 castalia Exp Copyright (C) 2001-2008 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Configuration.*; import java.sql.Connection; import java.util.Vector; /** A Data_Port is the interface for accessing the content of a Database.

@author Bradford Castalia, UA/PIRL @version 2.3 @see Database */ public interface Data_Port { /** Gets the Configuration of the Data_Port object.

@return The current Configuration of the Data_Port. @see Configuration */ public Configuration Configuration (); /** Gets the required and optional parameters used by the Data_Port.

The Configurtion that is returned will contain two groups: "Required" and "Optional" with lists of parameters naming those in each group. Each parameter may have a value indicating the default that will be used if the parameter is not provided in the Configuration used to {@link #Open(Configuration) Open} the Data_Port. A parameter without a value has no default; a Required parameter without a default must be in the provided Configuration or the Open will fail.

@return A Configuration of Required and Optional Data_Port parameters. @throws Database_Exception If the Data_Port could not be accessed. @throws Configuration_Exception If the database Configuration could not be accessed. */ public Configuration Parameters () throws Database_Exception, Configuration_Exception; /*============================================================================== Access */ /** Opens the Data_Port using the parameters from the Configuration to access a database.

@param configuration The Configuration parameters for access to a database through the Data_Port. @throws Database_Exception If the Data_Port could not be opened. @throws Configuration_Exception If the database Configuration could not be accessed. @see Configuration */ public void Open ( Configuration configuration ) throws Database_Exception, Configuration_Exception; /** Closes access to the database through the Data_Port.

@throws Database_Exception If there was a problem releasing any of the resources associated with the Data_Port. */ public void Close () throws Database_Exception; /** Tests if the Data_Port is currently open.

@return true if the Data_Port has access to a database; false otherwise. */ public boolean is_Open (); /** Gets the JDBC Connection object for the Data_Port, if there is one. There is no guarantee that any particular Data_Port will ever use a JDBC Connection object.

@return The JDBC Connection object for the Data_Port. This will be null if the Data_Port does not have a Connection, either because it does not have an active Connection or because it does not use a JDBC Connection . */ public Connection Connection (); /** Adds a SQL_Listener to the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be added. If null, or the listener is already listed, nothing is done. @return This Data_Port. @see SQL_Listener */ public Data_Port Add_SQL_Listener ( SQL_Listener listener ); /** Removes a SQL_Listener from the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be removed. @return true if the listener was removed; false if the listener was not listed. @see SQL_Listener */ public boolean Remove_SQL_Listener ( SQL_Listener listener ); /*============================================================================== Description */ /** Gets a one line String identifying the Data_Port.

@return The identifification line for the Data_Port. */ public String toString (); /** Gets a multi-line description of the Data_Port. If the Data_Port is currently {@link #Open Open}, this may include a description of the database connection.

@return The multi-line description of the Data_Port and any active database connection. */ public String Description (); /** Gets a String describing the contents of selected catalogs and tables.

For each table being described each field name and its data type is listed. Each Data_Port implementation may provide other details.

@param catalog The catalog to have its contents described. If it is in catalog.table format, the catalog portion of the name will be used. If null, all catalogs on the database server will be described. @param table The table to have its contents described. If it is in catalog.table format, the table portion of the name will be used. If null, all tables in the catalog (or all catalogs) will be described. @return A descriptive String. */ public String Contents ( String catalog, String table ); /** Gets the list of catalogs on the database server.

@return A Vector of catalog name Strings. @throws Database_Exception If the Data_Port is not open, or the operation on the database server failed. */ public Vector Catalogs () throws Database_Exception; /** Gets the list of tables in a catalog on the database server.

@param catalog The name of the catalog to be examined. If null, the {@link Database#CATALOG CATALOG} from the Data_Port Configuration will be used. @return A Vector of table name Strings. If the catalog does not exist in the database, null will be returned. An empty Vector will be returned for a catalog that does not contain any tables. @throws Database_Exception If the Data_Port is not open, or the operation on the database server failed. */ public Vector Tables ( String catalog ) throws Database_Exception; /** Gets the list of field names in a table on the database server.

@param table The name of the table to be examined. If null, then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field name Strings. If the table does not exist in the Database, null will be returned. @throws Database_Exception If the Data_Port is not open, no catalog name is available, or the operation on the database server failed. */ public Vector Field_Names ( String table ) throws Database_Exception; /** Gets the list of field data types in a table on the database server.

@param table The name of the table to be examined. If null, then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field type name Strings. If the table does not exist in the Database, null will be returned. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public Vector Field_Types ( String table ) throws Database_Exception; /** Obtains a list of field information for the table of a catalog.

The field_info tag corresponding to one of the field information names known to the database server. The possible tag lists should be available from the description of the particular Data_Port implementation being used, or in the database server documentation.

@param table The name of the table to be examined. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param field_info The tag String for selecting field information. @return A Vector of field information Strings, one per field. For info that is originally numeric (as indicated in the table) a String representation is returned. If the table does not exist in the Database, a null will be returned. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public Vector Fields ( String table, String field_info ) throws Database_Exception; /** Gets a list of primary keys for a table.

@param table The name of the table to be examined. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field name Strings. @throws Database_Exception If no catalog or table name is available, or the database server rejected the operation. */ public Vector Keys ( String table ) throws Database_Exception; /*============================================================================== Query */ /** Submit an SQL query.

The SQL_query String is an SQL statment that requests data to be extracted (not removed) from the database. The syntax, though "standardized", is likely to vary in its particulars dependent on the database server implementation.

A query always returns a table. The table is in the form of a Vector of data record Vectors. The first record contains the names (String) of each field for the data in corresponding locations of all the field value records that follow; i.e. these are the table column names. The field values in each data record are always provided in String representation regardless of the data type stored in the database. However, NULL field values remain unchanged as they are distinct from an otherwise valid field value.

Note: A field value will either be a String or null.

@param SQL_query The syntax of the query string is database dependent. A typical example is an SQL "SELECT" statement. @param limit The maximum number of records to return. If negative, there will be no limit to the number of records returned. If zero, no records will be returned. @return A data table in form of a Vector of String Vectors (records). The first record is the field names for the field values in all subsequent records. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public Vector> Query ( String SQL_query, int limit ) throws Database_Exception; /** Selects from one or more tables the data values from one or more named fields from the data records that satisfy the selection conditions.

Note: Each table name that is not in the format catalog.table will have the {@link Database#CATALOG CATALOG} from the Configuration prepended.

@param tables The Vector of database tables from which to select records. If this is null, the table specified in the Configuration will be used. @param fields A Vector of field name Strings specifying the data fields (columns) to be extracted. If this is null, all data fields will be extracted. @param conditions A String in SQL WHERE clause syntax (without the keyword "WHERE") specifying the conditions for selection of a data record from the database. The specific syntax of the conditions string is database dependent. If this is null, no conditions will be applied; all data records will be used. @param limit The maximum number of records to return. If negative, there will be no limit to the number of records returned. If zero, the returned vector will contain a single string that is the SQL statement that would have been submitted to the Query method. @return A Vector of String Vectors data table,as provided by the {@link #Query(String, int) Query} method. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public Vector> Select ( Vector tables, Vector fields, String conditions, int limit ) throws Database_Exception; /*============================================================================== Update */ /** Submit an SQL update.

The syntax of the SQL_update String is database dependent. These operations modify the database and return a count of the number of modifications, which may be 0 in some cases (e.g. when a catalog or table is created). Typical examples are SQL "UPDATE", "INSERT", and "ALTER" statements.

@param SQL_update The SQL_update statement. @return A count of the number of modifications. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public int Update ( String SQL_update ) throws Database_Exception; /*------------------------------------------------------------------------------ Catalogs: */ /** Creates a catalog in the database.

@param catalog The name of the catalog to create. A null catalog name, or a name of a catalog already in the database, is ignored. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public void Create ( String catalog ) throws Database_Exception; /** Deletes a catalog from the database.

@param catalog The name of the catalog to delete. A null catalog name, or a name that is not a catalog in the database, is ignored. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public void Delete ( String catalog ) throws Database_Exception; /*------------------------------------------------------------------------------ Tables: */ /** Creates data tables with their fields and the data types of the fields.

If the table does not exist, a new one is created. For each name String in the fields Vector, if the field does not exist, it is created with the data type from the corresponding entry in the types Vector. If the field is already present in the table, its data type is changed if needed.

A table name that is not in the format catalog.table will have the {@link Database#CATALOG CATALOG} from the Configuration prepended.

@param table The name of the table to be created. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be used. @param types The Vector of data type names to be applied to the corresponding fields. @throws Database_Exception If the Data_Port is not open, the number of fields and types are not the same, no table name is available, or the operation on the database server failed. @see #Configuration() */ public void Create ( String table, Vector fields, Vector types ) throws Database_Exception; /** Deletes fields from a data table, or the entire table.

Each field in the fields list is deleted from the table. If the field is not present in the table, it is ignored. If the fields list is null, the entire table is deleted from the catalog. N.B.: A table is not deleted even if all of its fields are deleted.

@param table The name of the table to be deleted. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be used. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. @see #Configuration() */ public void Delete ( String table, Vector fields ) throws Database_Exception; /** Renames a table.

If the table does not exist, nothing is done.

@param table The name of the table to be renamed. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param name The new name for the table. If this is null, nothing is done. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public void Rename ( String table, String name ) throws Database_Exception; /** Renames fields in a table.

Each field name in the fields Vector that exists in the table is renamed to the corresponding name in the names Vector. If the table does not exist, nothing is done.

Note: The existing type for the field, obtained from the {@link #Field_Types(String) Field_Types} method, is re-applied to the field (due to requirments of the SQL syntax). This could alter other characteristics of the field.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be renamed. @param names The Vector of new field names. @throws Database_Exception If the Data_Port is not open, the number of fields and names are not the same, no table name is available, or the operation on the database server failed. */ public void Rename ( String table, Vector fields, Vector names ) throws Database_Exception; /*------------------------------------------------------------------------------ Fields: */ /** Inserts values for selected fields into a table.

A new data record is created in the table. The fields Vector lists the names of the fields to be assigned a data value from the corresponding entry in the values Vector. The Vector of data values does not necessarily have to contain Strings, as long as each object's toString method produces a valid representation of the field value. Though the fields and values Vectors must be the same size, not all fields in the table need to be included; the database is expected to provide the default for missing fields.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names for the data values. @param values The Vector of data values corresponding the Vector of field names. @return The number of records inserted; 1 if successful; 0 otherwise. @throws Database_Exception If the Data_Port is not open, the number of fields and values are not the same, no table name is available, or the operation on the database server failed. */ public int Insert ( String table, Vector fields, Vector values ) throws Database_Exception; /** Updates values for selected fields into a table.

The fields Vector lists the names of the fields to be assigned a new data value from the corresponding entry in the values Vector. The Vector of data values does not necessarily have to contain Strings, as long as each object's toString method produces a valid representation of the field value. The records to have their field values updated will be selected by the conditions expression. This is an SQL WHERE expression (without the "WHERE" keyword). If no conditions are provided, then all records will be updated.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names for the data values. @param values The Vector of data values corresponding the Vector of field names. @param conditions A String in SQL WHERE clause syntax (without the keyword "WHERE") specifying the conditions for selection of a data record from the database. The specific syntax of the conditions string is database dependent. If this is null, no conditions will be applied; all data records will be used. @return The number of records that were modified. @throws Database_Exception If the Data_Port is not open, the number of fields and values are not the same, no table name is available, or the operation on the database server failed. */ public int Update ( String table, Vector fields, Vector values, String conditions ) throws Database_Exception; /** Deletes selected records from a table.

The records to be deleted will be selected by the conditions expression. This is an SQL WHERE expression (without the "WHERE" keyword).

Warning: If no conditions are provided, then all records will be deleted leaving an empty table.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} will be used. @param conditions A String in SQL WHERE clause syntax (without the keyword "WHERE") specifying the conditions for selection of a data record from the database. The specific syntax of the conditions string is database dependent. If this is null, no conditions will be applied and all data records will be deleted. @return The number of records that were deleted. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public int Delete ( String table, String conditions ) throws Database_Exception; /*============================================================================== Utility */ /** Generates a table reference from a catalog and table name.

A table reference in a Data_Port method may be a composite name combining a catalog name and a table name. The specific format for this combination is dependent on the syntax requirements of the database being used.

If a non-null, non-empty catalog name is specified it will override any catalog name portion that might be in a table name that is already in table reference format. The Data_Port may use appropriate defaults for null or empty table or catalog names; e.g. the value of {@link Database#CATALOG CATALOG} and {@link Database#TABLE TABLE} Configuration parameters. Though how defaults, if any, are provided is at the discretion of the Data_Port.

@param catalog The catalog name portion for the table reference. @param table The table name portion for the table reference. @return A table reference suitable for use as a table argument of a Data_Port method. @throws Database_Exception If a table reference can not be formed. */ public String Table_Reference ( String catalog, String table ) throws Database_Exception; /** Gets the catalog name portion of a table reference.

@param table_reference A String that may be a composite name combining a catalog name and a table name in a database specific format. @return The catalog name portion of the table reference. If the table reference does not have a catalog name portion the empty String is returned. @throws Database_Exception If the Data_Port is not open. @see #Table_Reference(String, String) */ public String Catalog_Name ( String table_reference ) throws Database_Exception; /** Gets the name known to the database server for a catalog name.

Only accessible catalogs can be identified. If the database server determines that the connection does not have permission to access the specified catalog then it can not be identified.

N.B.: Identifying the catalog from the list of accessible catalogs is done using the {@link #Case_Sensitive_Identifiers() case sensitivity} of the Data_Port implementation. Therefore, if case sensitive matching is used this method can only return the specified catalog name or null; in this case this method is only useful for determining if a catalog is accessible or not.

@param catalog The name of the database calolog to examine. If this is a {@link #Table_Reference(String, String) table reference} only the catalog part will be used. If null or empty the {@link Database#CATALOG CATALOG} value from the {@link #Configuration() configuration}, if available, will be used. @return The name of the catalog as it is known in the database. This will be null if a non-null, non-empty catalog name could not be identified in the database. @throws Database_Exception If the Data_Port is not open, a catalog name was not provided, or the database rejects the operation. */ public String Database_Catalog_Name ( String catalog ) throws Database_Exception; /** Gets the table name portion of a table reference.

@param table_reference A String that may be a composite name combining a catalog name and a table name in a database specific format. @return The table name portion of the table reference. If the table reference is null the empty String will be returned. @throws Database_Exception If the Data_Port is not open. @see #Table_Reference(String, String) */ public String Table_Name ( String table_reference ) throws Database_Exception; /** Gets the name known to the database server for a table name.

Only accessible catalogs can be identified. If the database server determines that the connection does not have permission to access the specified catalog then it can not be identified.

N.B.: Identifying the catalog from the list of accessible catalogs is done using the {@link #Case_Sensitive_Identifiers() case sensitivity} of the Data_Port implementation. Therefore, if case sensitive matching is used this method can only return the specified catalog name or null; in this case this method is only useful for determining if a catalog is accessible or not.

@param table The name of the table to identify in the database. If null, then the {@link Database#TABLE TABLE} value from the Configuration, if present, will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} value, if present, will be used. @return The name of the catalog as it is known in the database. This will be null if a catalog name could not be identified in the database. @throws Database_Exception If the Data_Port is not open, catalog and table names were not provided, or the database rejects the operation. */ public String Database_Table_Name ( String table ) throws Database_Exception; /** Gets the table reference component delimiter.

@return The portion of a table reference that delimits the catalog component from the table component. @throws Database_Exception If the Data_Port is not open. @see #Table_Reference(String, String) */ public String Table_Reference_Component_Delimiter () throws Database_Exception; /** Tests if identifier names are treated as case sensitive by the Data_Port.

Because some database servers on some operating systems are not case sensitive in handling identifiers (the names of catalogs, tables, and field names) it is necessary to enforce case insensitivity when matching user specified names against identifiers returned from the database server. This method indicates how the Data_Port expects identifier names to be treated.

N.B.: The identifier case sensitivity that is reported by the Data_Port does not necessarily reflect the identifier case sensitivity of the database server under all circumstances. Some identifiers may be case sensitive and others not, a given identifier may be case sensitive on some systems but not on others, an identifier may be case sensitive if double quoted but otherwise not (PostgreSQL); the rules are database implementation specific.

What the Case_Sensitive_Identifiers flag indicates is how indentifiers provided the by the user will be matched against identifiers provided by the database server.

@return true if identifiers will be matched with case sensitive comparisons; false if matching will ignore case. @see Database#Matches(String, String) */ public boolean Case_Sensitive_Identifiers () throws Database_Exception; /** Attempts to set whether identifier names are treated as case sensitive by the Data_Port.

N.B.: This setting does not itself determine the case sensitivity of the identifiers by the database server. The value is used to inform the Database software if case sensitive matches should be done. Setting the value to true when the database server uses case insensitive identifiers (mixed case identifiers provided by the user to the database server being always read back as all lowercase) will ultimately result in unexpected mismatches. Only set the value to true when it is known that the database server accessed by the Data_Port is capable of using case sensitive identifiers.

N.B.: A Data_Port implementation is free to ignore this setting, and may even through an exception if it thinks that the value would cause problems. In general, it is best to leave the case sensitive identifiers setting up to the Data_Port. Only when the application is aware of particular circumstances when using a particular database server that would call for a change of the Data_Port setting should the value be changed.

@param case_sensitive true if identifiers are to be matched with case sensitive comparisons; false if matching is to ignore case. @throws Database_Exception If the Data_Port can not be accessed, or the Data_Port rejects the setting. @see #Case_Sensitive_Identifiers() */ public void Case_Sensitive_Identifiers ( boolean case_sensitive ) throws Database_Exception; } pirl-2.3.8/PIRL/Database/Data_View.java0000644000175000017500000007541411742733571017351 0ustar mathieumathieu/* Data_View PIRL CVS ID: Data_View.java,v 1.14 2012/04/16 06:08:57 castalia Exp Copyright (C) 2002-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.PVL.*; import PIRL.Viewers.*; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.util.Vector; import java.util.Iterator; import java.io.File; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.IOException; import java.net.URL; /** Data_View manages access to Database_Views as mediated by its Configuration.

@author Bradford Castalia, UA/PIRL @version 1.14 @see Configuration */ public class Data_View extends JFrame { private static final String ID = "PIRL.Database.Data_View (1.14 2012/04/16 06:08:57)"; private static final Point DEFAULT_INITIAL_LOCATION = new Point (50, 25); private boolean Tooltips_Enabled = true; private JMenuItem Tooltips_Checkbox; private static final String TOOLTIPS_PARAMETER = "/Tooltips"; // Configuration. private static final String DEFAULT_CONFIGURATION_FILENAME = "Database.conf"; private String CWD // Current Working Directory. = System.getProperty ("user.dir"), Configuration_Filename = null; private JMenuItem Save_Menu_Item; private Configuration Default_Configuration = null, Current_Configuration; private JComboBox Server_Names; private Vector Servers, Removed_Parameters = new Vector (); private static final String REMOVE_PARAMETER = "/remove", MASKED_VALUE = "*******"; private static ImageIcon OPEN_ICON = null; private static final String OPEN_ICON_NAME = "Open_File16.gif"; private static ImageIcon SAVE_ICON; private static final String SAVE_ICON_NAME = "Save16.gif"; private static ImageIcon SAVE_AS_ICON; private static final String SAVE_AS_ICON_NAME = "SaveAs16.gif"; private static ImageIcon ABOUT_ICON; private static final String ABOUT_ICON_NAME = "About16.gif"; private static ImageIcon PIRL_ICON; private static final String PIRL_ICON_NAME = "PIRL_Logo.gif"; private static ImageIcon LPL_ICON; private static final String LPL_ICON_NAME = "LPL_Logo.gif"; private static ImageIcon UA_ICON; private static final String UA_ICON_NAME = "UofA_Logo.gif"; // Database. private Vector Database_Views = new Vector (); private View_Locator Database_View_Locator = new View_Locator (); private static final String DATABASE_TYPE_PARAMETER = Database.TYPE; // Connection. private JButton Connect_Button; private boolean Confirm_Connect = false; private JMenuItem Confirm_Connect_Checkbox; private static final String CONFIRM_CONNECT_PARAMETER = "/Confirm_Connect"; private static final Dimension PASSWORD_DIALOG_SIZE = new Dimension (250, 100); private static final String HOSTNAME_PARAMETER = Configuration.HOST, USERNAME_PARAMETER = Configuration.USER, PASSWORD_PARAMETER = "password", CLASSPATH_PARAMETER = Configuration.CLASSPATH; private Data_View This_Data_View; private static final char File_Separator = System.getProperty ("file.separator").charAt (0); private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_CONNECT = 1 << 1, DEBUG_CONFIG = 1 << 2, DEBUG_HELPERS = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Data_View ( Configuration configuration ) throws Configuration_Exception { super("Data_View"); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Data_View"); This_Data_View = this; Load_Icons (this); Font Default_Font = new Font ("SansSerif", Font.PLAIN, 12); setJMenuBar (Menu_Bar ()); Configure (configuration); getContentPane ().add (Panels (), BorderLayout.CENTER); pack (); } private Data_View () throws Database_Exception { throw new Database_Exception ( getClass().getName() + ": Hey! You shouldn't be using the default constructor." ); } /*============================================================================== Accessors */ public Vector Servers () {return Servers;} /*============================================================================== Menus */ private JMenuBar Menu_Bar () { JMenuBar menu_bar = new JMenuBar (); JMenu menu; JMenuItem menu_item; menu = new JMenu ("File"); menu_item = new JMenuItem ("Open", OPEN_ICON); menu_item.setMnemonic ('O'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('O', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_Configuration ();}}); menu.add (menu_item); Save_Menu_Item = new JMenuItem ("Save", SAVE_ICON); Save_Menu_Item.setMnemonic ('O'); Save_Menu_Item.setAccelerator (KeyStroke.getKeyStroke ('S', Event.CTRL_MASK)); Save_Menu_Item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Configuration (false);}}); menu.add (Save_Menu_Item); menu_item = new JMenuItem ("Save As ...", SAVE_AS_ICON); menu_item.setMnemonic ('A'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('O', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_Configuration (true);}}); menu.add (menu_item); menu.addSeparator (); menu_item = new JMenuItem ("Exit"); menu_item.setMnemonic ('X'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('Q', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener() {public void actionPerformed (ActionEvent event) {System.exit (0);}}); menu.add (menu_item); menu_bar.add (menu); menu = new JMenu ("Preferences"); Confirm_Connect_Checkbox = new JCheckBoxMenuItem ("Confirm Connection", Confirm_Connect); Confirm_Connect_Checkbox.setMnemonic ('C'); Confirm_Connect_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Flip_Confirm_Connect ();}}); menu.add (Confirm_Connect_Checkbox); Tooltips_Checkbox = new JCheckBoxMenuItem ("Tooltips", Tooltips_Enabled); Tooltips_Checkbox.setMnemonic ('T'); Tooltips_Checkbox.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Flip_Tooltips_Enabled ();}}); menu.add (Tooltips_Checkbox); menu_bar.add (menu); menu = new JMenu ("Help"); menu_item = new JMenuItem ("About ...", ABOUT_ICON); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {About (This_Data_View);}}); menu.add (menu_item); menu_bar.add (menu); return menu_bar; } /*============================================================================== Panels */ private JPanel Panels () { JPanel panel = new JPanel (); GridBagLayout grid = new GridBagLayout (); GridBagConstraints location = new GridBagConstraints (); panel.setLayout (grid); // Connect button: Connect_Button = new JButton ("Connect"); Connect_Button.setMnemonic ('C'); Connect_Button.setToolTipText ("Open a connection to the database."); Connect_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Connect_to_Database ();}}); Connect_Button.setDefaultCapable (true); getRootPane ().setDefaultButton (Connect_Button); location.insets = new Insets (10, 10, 10, 5); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; panel.add (Connect_Button, location); // Type: location.insets = new Insets (10, 0, 10, 5); location.fill = GridBagConstraints.NONE; panel.add (new JLabel("to server:"), location); Server_Names = new JComboBox (Servers); Server_Names.setEnabled (! Servers.isEmpty ()); Server_Names.setMinimumSize (new Dimension (150, 25)); Server_Names.setPreferredSize (new Dimension (150, 25)); Server_Names.setToolTipText ("The name of a database server."); location.insets = new Insets (10, 0, 10, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; panel.add (Server_Names, location); return panel; } /*============================================================================== Configuration */ private void Configure ( Configuration configuration ) throws Configuration_Exception { Configuration default_configuration; if (Default_Configuration == null) { // First time: Load the class's default configuration. try { Default_Configuration = new Configuration (ClassLoader.getSystemResource (getClass ().getPackage ().getName ().replace ('.', File_Separator) + File_Separator + DEFAULT_CONFIGURATION_FILENAME)); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Default Configuration -" + NL + Default_Configuration.Description ()); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to load the default Configuration." + NL + NL + ID + NL + exception.getMessage ()); throw exception; } // Get any list of parameters to be removed. Removed_Parameters = Default_Configuration.Get_Specific (REMOVE_PARAMETER); // Remove passwords. if (! Removed_Parameters.contains (PASSWORD_PARAMETER)) Removed_Parameters.add (PASSWORD_PARAMETER); } if (configuration == null) Current_Configuration = new Configuration (Default_Configuration); else Current_Configuration = configuration; // Configuration flags. boolean case_sensitive = Current_Configuration.Case_Sensitive (false); Tooltips_Checkbox.setSelected (Tooltips_Enabled = get_flag (Current_Configuration, TOOLTIPS_PARAMETER, Tooltips_Enabled)); Confirm_Connect_Checkbox.setSelected (Confirm_Connect = get_flag (Current_Configuration, CONFIRM_CONNECT_PARAMETER, Confirm_Connect)); // Server names. Servers = Current_Configuration.Get (Configuration.Absolute_Pathname (null, Database.SERVER)); if (Servers.isEmpty ()) Servers = Current_Configuration.Get (Configuration.Absolute_Pathname (null, Database.TYPE)); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Server names: " + Servers); // Check for a local filename. String Configuration_Filename = Current_Configuration.Filename (); if (Configuration_Filename == null || Configuration_Filename.equals (Configuration.DEFAULTS)) // No filename. Configuration_Filename = null; else if (Configuration_Filename.startsWith ("file:" + File_Separator)) // file:/ URL; use the pathname portion. Configuration_Filename = Configuration_Filename.substring (5); else { int index = Configuration_Filename.indexOf (':'); if (index > 0 && Configuration_Filename.indexOf (File_Separator) == (index + 1)) // URL Configuration_Filename = null; } if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Configuration_Filename - " + Configuration_Filename); Set_Savable (Configuration_Filename != null); Current_Configuration.Case_Sensitive (case_sensitive); } public static boolean get_flag ( Configuration configuration, String parameter, boolean default_flag ) { String value = configuration.Get_One (parameter); if (value == null) return default_flag; if (value.equalsIgnoreCase ("on") || value.equalsIgnoreCase ("true") || value.equalsIgnoreCase ("enabled")) return true; else return false; } /*============================================================================== Actions */ // OPEN a new Configuration file. private void Open_Configuration () { JFileChooser file_chooser = new JFileChooser (CWD); file_chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); if (file_chooser.showOpenDialog (rootPane) == JFileChooser.APPROVE_OPTION) { File file = file_chooser.getSelectedFile (); CWD = file.getParent (); try { Configure (new Configuration (file.getPath ())); // Reset the server names. Server_Names.removeAllItems (); if (Servers.isEmpty ()) Server_Names.setEnabled (false); else for (Iterator names = Servers.iterator (); names.hasNext (); Server_Names.addItem (names.next ())); } catch (Exception exception) { Dialog_Box.Error ("Unable to Open the Configuration -" + NL + file.getPath () + NL + NL + exception.getMessage (), this); } } } // SAVE the Configuration to a file. private void Save_Configuration ( boolean interactive ) { // Make sure the configuration has all the settings. try { Current_Configuration.Set (TOOLTIPS_PARAMETER, (Tooltips_Enabled ? "true" : "false")); Current_Configuration.Set (CONFIRM_CONNECT_PARAMETER, (Confirm_Connect ? "true" : "false")); Current_Configuration.Remove (CLASSPATH_PARAMETER); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to set the configuration values!" + NL + NL + ID + NL + exception.getMessage ()); return; } File file; if (! interactive && Configuration_Filename == null) // No filename; force interactive operation. interactive = true; if (interactive) { JFileChooser file_chooser = new JFileChooser (CWD); file_chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); if (file_chooser.showSaveDialog (this) != JFileChooser.APPROVE_OPTION) return; file = file_chooser.getSelectedFile (); CWD = file.getParent (); } else file = new File (Configuration_Filename); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" Data_View.Save_File_As: " + file.getPath ()); // Write the configuration file. BufferedWriter writer = null; String name = Current_Configuration.Name (); try { writer = new BufferedWriter (new FileWriter (file)); writer.write (Parser.CROSSHATCH); writer.write (' '); writer.write (name + NL); writer.write (Parser.CROSSHATCH); writer.write (' '); writer.write (ID + NL); } catch (IOException exception) { Dialog_Box.Error ("Unable to save the configuration file -" + NL + file.getPath () + NL + NL + ID + NL + exception.getMessage ()); } Lister lister = new Lister (writer).Strict (false); // Prevent the top level from being written. Current_Configuration.Name (Parser.CONTAINER_NAME); try { lister.Write (Current_Configuration); Configuration_Filename = file.getPath (); Set_Savable (false); } catch (PVL_Exception exception) { Dialog_Box.Error ("The configuration contains an invalid parameter!" + NL + NL + ID + NL + exception.getMessage ()); Current_Configuration.Name (name); // Restore the original name. } catch (IOException exception) { Dialog_Box.Error ("Unable to write the configuration to the file -" + NL + file.getPath () + NL + NL + ID + NL + exception.getMessage ()); Current_Configuration.Name (name); // Restore the original name. } if (lister.Warning () != null) { Dialog_Box.Warning ("Potential syntax problem in the configuration file -" + NL + file.getPath () + NL + NL + ID + NL + lister.Warning ().getMessage () + NL); } } // Enable/Disable the Save menu item in the File menu. private void Set_Savable ( boolean savable ) { if (savable && Configuration_Filename == null) savable = false; Save_Menu_Item.setEnabled (savable); } /*------------------------------------------------------------------------------ CONNECT to a Database and provide a new Database_View. */ private void Connect_to_Database () { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> Connect_to_Database"); Database database = null; try { Configuration configuration = new Configuration (Current_Configuration); database = new Database (configuration); // The the Configuration to Open the database connection. if ((configuration = Get_Connection_Info (database)) == null) return; // User cancelled. database.Open (configuration); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to work with the application's configuration." + NL + NL + ID + NL + Masked_Message (Removed_Parameters, exception.getMessage ())); return; } catch (Database_Exception exception) { Dialog_Box.Error ("Unable to Connect to the Database." + NL + NL + ID + NL + Masked_Message (Removed_Parameters, exception.getMessage ())); return; } // Construct a new Database_View. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Connect_to_Database: Creating a new Database_View"); final Database DB = database; final Blinker blinking = new Blinker (Connect_Button); blinking.start (); final Cursor cursor = getCursor (); setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR)); Thread construct_Database_View = new Thread () { public void run () { try { Database_View database_view = new Database_View (DB); database_view.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent event) { Window window = event.getWindow (); Database_Views.remove (window); window.dispose (); }}); // Set the new screen location. if (Database_Views.isEmpty ()) { // First Database_View. Database_View_Locator .Offsets (0, 0) .Horizontal (View_Locator.LEFT | View_Locator.INWARD) .Vertical (View_Locator.BOTTOM | View_Locator.OUTWARD); // Locate relative to this view. Database_View_Locator.Relocate (database_view, This_Data_View); } else { Database_View_Locator.Policies (null); // Defaults. // Locate relative to the last Database_View. Database_View_Locator.Relocate (database_view, (JFrame)Database_Views.lastElement ()); } // Remember it and view it. Database_Views.add (database_view); database_view.setVisible (true); Set_Savable (true); } catch (Exception exception) { Dialog_Box.Error ("Unable to create the new Database_View." + NL + NL + ID + NL + Masked_Message (Removed_Parameters, exception.getMessage ())); } SwingUtilities.invokeLater (new Runnable () { public void run () { blinking.stop (); setCursor (cursor); }}); }}; construct_Database_View.start (); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Connect_to_Database"); } private void Flip_Tooltips_Enabled () { ToolTipManager.sharedInstance ().setEnabled (Tooltips_Enabled = ! Tooltips_Enabled); Set_Savable (true); } private void Flip_Confirm_Connect () { Confirm_Connect = ! Confirm_Connect; Set_Savable (true); } /*============================================================================== Helpers */ private Configuration Get_Connection_Info ( Database database ) { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> Get_Connection_Info"); String server = null; if (! Servers.isEmpty ()) server = (String)Server_Names.getSelectedItem (); Configuration database_configuration = null; try {database_configuration = database.Server_Configuration (server);} catch (Configuration_Exception e) {/* Shouldn't happen */} if (database_configuration == null) database_configuration = database.Configuration (); if (server == null) server = ""; String hostname = database_configuration.Get_One (HOSTNAME_PARAMETER), username = database_configuration.Get_One (USERNAME_PARAMETER), password = database_configuration.Get_One (PASSWORD_PARAMETER); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Get_Connection_Info:" + NL +" Database Configuration -" + NL + database_configuration.Description ()); Connect_Info: if (Confirm_Connect || (hostname == null) || (username == null) || (password == null)) { JPanel panel = new JPanel (); GridBagLayout grid = new GridBagLayout (); GridBagConstraints location = new GridBagConstraints (); panel.setLayout (grid); // Server hostname. JTextField hostname_field = new JTextField (hostname); location.gridwidth = 1; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; panel.add (new JLabel("Server name:"), location); panel.add (Box.createHorizontalStrut (5), location); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; panel.add (hostname_field, location); // User name. JTextField username_field = new JTextField (username); location.gridwidth = 1; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; panel.add (new JLabel("User name:"), location); panel.add (Box.createHorizontalStrut (5), location); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; panel.add (username_field, location); // Password. JPasswordField password_field = new JPasswordField (password); location.gridwidth = 1; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; panel.add (new JLabel("Password:"), location); panel.add (Box.createHorizontalStrut (5), location); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; panel.add (password_field, location); JLabel warning = new JLabel ("For the database, NOT the system"); warning.setFont (new Font ("SansSerif", Font.PLAIN, 10)); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; panel.add (warning, location); panel.setPreferredSize (PASSWORD_DIALOG_SIZE); if (JOptionPane.showConfirmDialog ( null, panel, ("Connect to Server: " + server), JOptionPane.OK_CANCEL_OPTION ) == JOptionPane.OK_OPTION) { hostname = hostname_field.getText ().trim (); if (hostname.length () == 0) { Dialog_Box.Notice ("A server host name to connect to is required."); break Connect_Info; } username = username_field.getText ().trim (); if (username.length () == 0) { Dialog_Box.Notice ("A database user name is required."); break Connect_Info; } password = new String (password_field.getPassword ()); if (password.length () == 0) { Dialog_Box.Notice ("A database password is required."); break Connect_Info; } try { database_configuration.Set (HOSTNAME_PARAMETER, hostname); database_configuration.Set (USERNAME_PARAMETER, username); database_configuration.Set (PASSWORD_PARAMETER, password); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Get_Connection_Info:" + NL +" hostname: " + hostname + NL +" username: " + username + NL +" password: " + password + NL +" Configuration -" + NL + database_configuration.Description ()); } catch (Configuration_Exception exception) { Dialog_Box.Error ("Unable to set the configuration parameters." + NL + NL + ID + NL + Masked_Message (Removed_Parameters, exception.getMessage ())); } } else { database_configuration = null; if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Get_Connection_Info: cancelled."); } } return database_configuration; } public static String Masked_Message ( Vector removed_parameters, String message ) { if (message == null) return ""; if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (">>> Masked_Message:" + NL + message + NL); StringBuffer masked_message = new StringBuffer (message); int patch_size = MASKED_VALUE.length (), stop = masked_message.length (); Iterator parameters = removed_parameters.iterator (); while (parameters.hasNext ()) { String word = (String)parameters.next (); if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (" Removed parameter " + word); int length = word.length (), start = 0, end; while ((start = masked_message.indexOf (word, start)) != -1) { /* Found the name of a value to be masked. Skip it and following whitespace and '=' characters. */ for (start += length, end = 0; start != stop && (masked_message.charAt (start) == ' ' || masked_message.charAt (start) == '='); start++) { if (masked_message.charAt (start) == '=') { if (end == 1) break; end = 1; } } if (start == stop) break; if (end == 0) continue; // Find the end of the value to be obscured. for (end = start + 1; end != stop && (masked_message.charAt (end) != ' ' && masked_message.charAt (end) != '\n' && masked_message.charAt (end) != ')'); end++); // Obscure it. if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (" Masked: " + start + " - " + end); masked_message.replace (start, end, MASKED_VALUE); // Adjust the known message length. stop += patch_size - (end - start); start = end; } } if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println ("<<< Masked_Message:" + NL + masked_message); return masked_message.toString (); } private static void Load_Icons ( Object object ) { if (OPEN_ICON != null) return; String package_name = object.getClass ().getName (); package_name = package_name.substring (0, package_name.lastIndexOf ('.')) .replace ('.', File_Separator) + File_Separator + "Icons" + File_Separator; URL iconURL; iconURL = ClassLoader.getSystemResource (package_name + OPEN_ICON_NAME); if (iconURL != null) OPEN_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + SAVE_ICON_NAME); if (iconURL != null) SAVE_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + SAVE_AS_ICON_NAME); if (iconURL != null) SAVE_AS_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + ABOUT_ICON_NAME); if (iconURL != null) ABOUT_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + PIRL_ICON_NAME); if (iconURL != null) PIRL_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + LPL_ICON_NAME); if (iconURL != null) LPL_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + UA_ICON_NAME); if (iconURL != null) UA_ICON = new ImageIcon (iconURL); } public static void About ( JFrame parent ) { final JFrame frame = new JFrame ("PIRL Data_View"); frame.setDefaultCloseOperation (WindowConstants.DISPOSE_ON_CLOSE); JPanel content = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); location.insets = new Insets (10, 10, 20, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; content.add (new JLabel ("This is a Beta Release: " + ID + ""), location); location.insets = new Insets (0, 10, 5, 0); location.gridwidth = 1; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; content.add (new JLabel (PIRL_ICON), location); location.insets = new Insets (0, 10, 5, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; content.add (new JLabel ("" +"Planetary Image

Research Laboratory"), location); location.insets = new Insets (0, 10, 5, 0); location.gridwidth = 1; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; content.add (new JLabel (LPL_ICON), location); location.insets = new Insets (0, 10, 5, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; content.add (new JLabel ("" +"Department of

Planetary Sciences"), location); location.insets = new Insets (0, 10, 5, 0); location.gridwidth = 1; location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; content.add (new JLabel (UA_ICON), location); location.insets = new Insets (0, 10, 5, 10); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; content.add (new JLabel ("" +"University of Arizona

Tucson, Arizona"), location); location.insets = new Insets (20, 10, 10, 10); location.fill = GridBagConstraints.BOTH; location.anchor = GridBagConstraints.NORTHWEST; location.weightx = 1.0; location.weighty = 1.0; content.add (new JLabel ("" +"Comments - including recommendations for changes," + NL +"error reports (please include the full text of any error messages)" + NL +"or any other communication -" + NL +"can be sent to the author, Bradford Castalia, at Castalia@Arizona.edu." + NL), location); content.setPreferredSize (new Dimension (375, 400)); frame.setContentPane (content); frame.pack (); View_Locator locator = new View_Locator () .Orientation ( View_Locator.RIGHT | View_Locator.OUTWARD, View_Locator.TOP | View_Locator.INWARD) .Offsets (0, 0); locator.Relocate (frame, parent); frame.setVisible (true); } /*============================================================================== Application */ public static void main (String[] arguments) { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("*** Data_View ***"); try { Configuration configuration; if (arguments.length > 0) configuration = new Configuration (arguments[0]); else configuration = new Configuration (DEFAULT_CONFIGURATION_FILENAME); if ((DEBUG & DEBUG_CONFIG) != 0) System.out.println (" User Configuration -" + NL + configuration.Description ()); if (configuration.Filename ().equals (Configuration.DEFAULTS)) // Only got defaults; leave Data_View to load its defaults. configuration = null; Data_View data_view = new Data_View (configuration); data_view.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); // Set the new screen location. data_view.setLocation (DEFAULT_INITIAL_LOCATION); data_view.setVisible (true); if (! data_view.Servers ().isEmpty ()) { // Open the default Database. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (" Connecting to the Database..."); data_view.Connect_to_Database (); } } catch (Exception exception) { System.out.println (exception.getMessage ()); System.exit (1); } } } // End of Data_View class. pirl-2.3.8/PIRL/Database/Database.java0000644000175000017500000014237511742733571017213 0ustar mathieumathieu/* Database PIRL CVS ID: Database.java,v 2.35 2012/04/16 06:08:57 castalia Exp Copyright (C) 2001-2008 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import java.sql.Connection; import java.util.Vector; import java.util.Hashtable; import java.util.Iterator; import java.util.Collection; /** A Database provides an implementation of a Data_Port to manage access to any database for which a driver is available.

Every Database will have a Configuration. If one is not specified when a Database object is created a default Configuration is created. The application may provide a Configuration directly to the new Database object. The Configuration is a group of PVL Parameters that provide needed characterization for accessing specific databases.

A Database is like a File in that instantitating an object of this class does not establish a data access connection to a database's contents. This is done with a {@link #Connect(String, String) Connect} method. To Connect to a database server its type (e.g. "MySQL") must be specified or a default {@link #TYPE TYPE} parameter must be found in the Configuration. The Database will extract from its Configuration only those Parameters applicable to the specified type and use them to {@link #Open(Configuration) Open} the Data_Port. The Open method will load a Data_Port class, having a name beginning with the database type name and ending with "_Data_Port", that implements the Data_Port interface and knows how to access that type of database (it may, for example, load a JDBC driver class, perhaps named by a "Driver" parameter). The Database class implements the Data_Port interface itself by passing its Data_Port method calls on to its current dynamically loaded Data_Port object.

If a specific database catalog to access from the server is specified with the Connect method, then a {@link #CATALOG CATALOG} parameter will be set in the Data_Port Configuration. The Data_Port is likely to supplement the Configuration it is given with its own default Parameters.

There are a set of parameter names that are known to the Database class:

{@link #SERVER Server}
The list of server configuration groups in a Configuration. Each group must have the name of one of the names in the Server list. The first name in the list is the default server.
{@link #TYPE Type}
The type of database server to connect to. This name, with the addition of "_Data_Port", is the name of the Data_Port class providing access to the server. There is no default; it must be specified.
{@link #DRIVER Driver}
The formal name, as a Java pathname, of a database driver class that might be needed by a Data_Port. Usually each Data_Port class implementation provides its own default.
{@link #CATALOG Catalog}
The name of the data catalog to use on the server.
{@link #TABLE Table}
The name of one or more tables within a catalog to be accessed for data. The first table listed is the default table.
{@link #FIELDS Fields}
The name of one or more fields within a table.

@see Configuration @see Data_Port @author Bradford Castalia - UA/PIRL @version 2.35 */ public class Database implements Data_Port { /** Class name and version identification. */ public static final String ID = "PIRL.Database.Database (2.35 2012/04/16 06:08:57)"; /** The parameter name for the default database server: "Server". */ public static final String SERVER = "Server"; /** The parameter name for the type of database server which corresponds to the Data_Port class to use: "Type". */ public static final String TYPE = "Type"; /** The parameter name for the driver class to provide access to the database server: "Driver". */ public static final String DRIVER = "Driver"; /** The parameter name for the name of the database on the server to be used: "Database".

N.B.: Some database servers require the value of this parameter to make a client connection (e.g. PostgreSQL), while others do not use it (e.g. MySQL).

@see #CATALOG */ public static final String DATABASE = "Database"; /** The parameter name for a container of data tables: "Catalog".

N.B.: The meaning of the term "catalog" to the Database class is the database server structure that contains a collection of data tables. For some database servers the corresponding term is "database" (e.g. MySQL), while for others it is "schema" (e.g. PostgreSQL). */ public static final String CATALOG = "Catalog"; /** The parameter name for the name of one or more tables within a catalog: "Table". */ public static final String TABLE = "Table"; /** The parameter name for the name of one or more fields within a table: "Field". */ public static final String FIELDS = "Fields"; /** The "value" to specify when setting a field to NULL. */ public static final String NULL_VALUE = "NULL"; /** The default for determining if database entity identifiers are case sensitive.

A database server that does not use case sensitive entity identifiers will coerce the identifiers to lowercase. When these identifiers are returned from a query they will not match user specified identifiers that are in mixed case.

Because some database servers on some operating systems are not case sensitive in handling identifiers (the names of catalogs, tables, and field names) it may be necessary to enforce case insensitivity when matching user specified names against identifiers returned from the database server.

When a Data_Port is {@link #Open(Configuration) open}ed this flag is initialized from the Data_Port {@link Data_Port#Case_Sensitive() case sensitive} indicator.

N.B.: The setting of this flag does not itself determine the case sensitivity of the identifiers, rather it is expected to inform the Database software if case sensitive matches are allowed. Setting this flag to true when the database server uses case insensitive identifiers (mixed case identifiers provided by the user to the database server being always read back as all lowercase) will ultimately result in unexpected mismatches. Only set this flag to true when it is known that all database servers that the Database class will be used with are case sensitive.

Unless case sensitive matches are required case insensitive identifier comparisions should be used.

@see #Matches(String, String) */ private static boolean Case_Sensitive_Default = false; /** The default configuration filename: "Database.conf". */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Database.conf"; private Configuration The_Configuration; private Data_Port Data_Port = null; private static final String DATA_PORT_CLASS_NAME_SUFFIX = "_Data_Port"; /** The new-line sequence for the local host system. */ public static final String NL = System.getProperty ("line.separator"); // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_CONFIGURE = 1 << 0, DEBUG_DESCRIPTION = 1 << 1, DEBUG_QUERY = 1 << 2, DEBUG_UTILITY = 1 << 3, DEBUG_LOAD = 1 << 4, DEBUG_CONNECT = 1 << 5, DEBUG_VALUE = 1 << 6, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates a Database with a default Configuration. */ public Database () throws Database_Exception { try {The_Configuration = new Configuration (DEFAULT_CONFIGURATION_FILENAME);} catch (Configuration_Exception exception) {throw new Database_Exception (exception);} } /** Creates a Database with a specific Configuration.

@param configuration The Configuration to use when {@link #Connect(String, String) connecting} to the database server. If the configuration is null, a default Configuration is created. */ public Database ( Configuration configuration ) throws Database_Exception { try { The_Configuration = (configuration == null) ? new Configuration (DEFAULT_CONFIGURATION_FILENAME) : configuration; } catch (Configuration_Exception exception) { throw new Database_Exception (exception); } } /*============================================================================== Accessors */ /** Gets the Configuration for the Database.

@return The Configuration of the Database. */ public Configuration Configuration () {return The_Configuration;} /** Gets the current Data_Port backing the Database.

@return The Data_Port currently being used by the Database. This will be null if the Database does not have a connection to a database server. */ public Data_Port Data_Port () {return Data_Port;} /*============================================================================== Methods */ /*-------------------------------------------------------------------------- Connect: */ /** Connects the Database to a named database server and a specific catalog of the database as a data source.

To connect to a database server the Configuration parameters used to {@link Database#Open(Configuration) Open} the connection must be found in the Configuration of this Database in a {@link Configuration#Group(String) Group} with the database server name. The case sensitivity of the search for the server name is controlled by the Configuration {@link Configuration#Case_Sensitive() Case_Sensitive} setting. If the name of this Database Configuration matches the server name, then the entire Configuration is used. If the server name is null, then the value of the top_level {@link #SERVER SERVER} parameter will be used, if present; otherwise the value of the top level {@link #TYPE TYPE} parameter will be used.

Note: The use of the TYPE parameter is deprecated; it is provided for backwards compatibility with previous versions of the Database configuration rules in which the database server was known by its TYPE name.

If a non-null catalog name is provided, a {@link #CATALOG CATALOG} parameter is set in the server Configuration group with this name.

Once the server Configuration group has been setup it is used to Open the Data_Port of this Database.

@param server The name of the database server to use. If null the {@link #Default_Server_Name() default server name} is used. @param catalog The name of database catalog to use. This may be null if no specific catalog is selected. @return This Database. @throws Configuration_Exception If the database server name can not be found in the Configuration, or its server group does not contain a {@link #TYPE TYPE} parameter. @throws Database_Exception If the Data_Port could not be Opened. @see Configuration#Group(String) @see #Open(Configuration) */ public Database Connect ( String server, String catalog ) throws Database_Exception, Configuration_Exception { if ((DEBUG & (DEBUG_CONNECT | DEBUG_CONFIGURE)) != 0) System.out.println (">>> Database.Connect:" + NL +" server: " + server + NL +" catalog: " + catalog + NL +" configuration -" + NL + The_Configuration); // Get the Configuration for the database server. Configuration configuration = Server_Configuration (server); if (configuration == null) { if (server == null && (server = Default_Server_Name ()) == null) { throw new Configuration_Exception ( ID + NL + "To Connect to a database a server name must be specified." + NL + "The \"" + SERVER + "\" parameter in a Configuration may be used to do this." ); } throw new Configuration_Exception ( ID + NL + "Unable to Connect to the database server." + NL + "Missing \"" + server + "\" Configuration Group." ); } if ((catalog != null && catalog.length () != 0)) { // Set the CATALOG parameter. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Database.Connect: Set " + CATALOG + " = " + catalog); configuration.Set (CATALOG, catalog); } // Open the data port. Open (configuration); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Database.Connect"); return this; } /** Connects the Database to a named database server.

@param server The name of the database server to use. @return This Database. @throws Database_Exception If establishing the Data_Port Configuration failed, or there was a problem opening the Data_Port. @see #Connect(String, String) */ public Database Connect ( String server ) throws Database_Exception, Configuration_Exception {return Connect (server, null);} /** Connects the Database to the default database server.

@return This Database. @throws Database_Exception If establishing the Data_Port Configuration failed, or there was a problem opening the Data_Port. @see #Connect(String, String) */ public Database Connect () throws Database_Exception, Configuration_Exception {return Connect (null, null);} /** Disconnect the Database (its backing Data_Port) from its database server.

If the Data_Port is already closed, nothing is done. */ public void Disconnect () throws Database_Exception { if (Data_Port != null && Data_Port.is_Open ()) Data_Port.Close (); Data_Port = null; } // Load the Data_Port class. private Data_Port Load_Data_Port ( String type ) throws Database_Exception { if ((DEBUG & DEBUG_LOAD) != 0) System.out.println (">>> Database.Load_Data_Port: type = " + type ); Data_Port data_port = null; String data_port_class = type + DATA_PORT_CLASS_NAME_SUFFIX; try { data_port = (Data_Port)Class.forName (data_port_class).newInstance (); } catch (ClassNotFoundException exception) { // Try again in this package. data_port_class = "PIRL.Database." + data_port_class; try { data_port = (Data_Port)Class.forName (data_port_class).newInstance (); } catch (ClassNotFoundException sigh) { throw new Database_Exception ( ID + NL + "Unable to find a \"" + type + DATA_PORT_CLASS_NAME_SUFFIX + "\" class." ); } catch (Exception bummer) {} } catch (Exception bummer) {} if (data_port == null) { throw new Database_Exception ( ID + NL + "Unable to load the \"" + data_port_class + "\" class." ); } if ((DEBUG & DEBUG_LOAD) != 0) System.out.println ("<<< Database.Load_Data_Port: " + data_port); return data_port; } /** Gets the required and optional parameters used by a Data_Port type.

@param type The name of the Data_Port type. If null, the currently connected Data_Port will be queried. @return A Configuration of Required and Optional Data_Port parameters. @throws Database_Exception If the Data_Port class can not be loaded. @throws IllegalArgumentException If the Data_Port type is null and there is no currently connected Data_Port. @throws Configuration_Exception If the Data_Port could not be loaded. @see Data_Port#Parameters() */ public Configuration Parameters ( String type ) throws Database_Exception, Configuration_Exception { Data_Port data_port; if (type == null) { if (Data_Port == null) throw new Database_Exception ( ID + NL + "Unable to get Parameters for an unknown type of Data Port." ); else data_port = Data_Port; } else data_port = Load_Data_Port (type); return data_port.Parameters (); } /** Gets the required and optional parameters used by the default Data_Port.

@see #Parameters(String) */ public Configuration Parameters () throws Database_Exception, Configuration_Exception {return Parameters (null);} /** Gets the default server name from the Database Configuration.

If a {@link #SERVER Server} parameter is not found in the {@link #Configuration() Configuration} of the Database the {@link #TYPE Type} parameter is obtained. These parameters must be at the top level of the Configuration.

@return The default server name for use in making a {@link #Connect(String, String) connection} to a database server. This will be null if the Database Configuration does not contain the required information. */ public String Default_Server_Name () { String server = The_Configuration.Get_One (Configuration.Absolute_Pathname (null, SERVER)); if (server == null) server = The_Configuration.Get_One (Configuration.Absolute_Pathname (null, TYPE)); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">-< Database.Default_Server_Name: " + server); return server; } /** Gets a copy of the configuration for a database server.

The server name is expected to be the name of a Configuration Group in the Database Configuration.

If the server name matches the name of the Database Configuration a copy of the entire Database Configuration is returned. Otherwise a copy of the first Group in the Database Configuration with the server name is returned.

@param server The server name. If null the {@link #Default_Server_Name() default server name} is used. @return A Configuration for the server. This will be null if the server name is null and no default server could be identified, or if no Group with the server name could be found. @throws Configuration_Exception If there was a problem while manipulating a Configuration. @see #Configuration() */ public Configuration Server_Configuration ( String server ) throws Configuration_Exception { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> Database.Server_Configuration: " + server); if (server == null && (server = Default_Server_Name ()) == null) { // No default server name. if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Database.Server_Configuration"); return null; } Configuration configuration = null; if (The_Configuration.Case_Sensitive () ? The_Configuration.Name ().equals (server) : The_Configuration.Name ().equalsIgnoreCase (server)) { // Use the entire Configuration. if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (" Database.Server_Configuration: Using entire Configuration - " + The_Configuration.Name ()); configuration = new Configuration (The_Configuration); } else { configuration = The_Configuration.Group (server); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (" Database.Server_Configuration: " + ((configuration == null) ? "couldn't find" : " found") +" Configuration Group " + server); } if (configuration != null) // Ensure there is a TYPE parameter; use the server name by default. configuration.Set_Conditionally (TYPE, server); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< Database.Server_Configuration"); return configuration; } /** Gets the default server configuration.

@return A Configuration for the default server. This will be null if no default server can be found. @throws Configuration_Exception If there was a problem while manipulating a Configuration. @see #Server_Configuration(String) */ public Configuration Server_Configuration () throws Configuration_Exception {return Server_Configuration (null);} /*============================================================================== Data_Port implementation */ /*------------------------------------------------------------------------------ Access: */ /** Opens a Data_Port using the Configuration.

If the Data_Port for this Database is already open, it is closed and then re-opened.

Before a Data_Port can be opened its implementing class must be loaded. The configuration must contain a {@link #TYPE Type} parameter which is used to select the Data_Port class that will be loaded to manage access to the database server. The name of this class must be "type"_Data_Port, where "type" is the value of the Configuration TYPE parameter. If the class is not found in the current classpath, it is presumed to be in PIRL.Database package. The Open method of the new Data_Port instance will be given the configuration to use in establishing its connection to the database server.

Note: The configuration of the Data_Port may be completely separate from the configuration associated with this Database; the preferred method to open access to a database is to use one of the {@link #Connect(String, String) Connect} methods.

@param configuration The Configuration for the Data_Port. If the configuration is null the default {@link #Connect() Connect} method is used to select a database type and its configuration. @throws Database_Exception If the TYPE parameter could not be found in the configuration; the implementing Data_Port class could not be loaded; or the Data_Port Open failed. */ public void Open ( Configuration configuration ) throws Database_Exception, Configuration_Exception { if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (">>> Database.Open: Configuration - " + configuration.Name ()); if (configuration == null) { // Use the default configuration, which will recursively call Open. Connect (); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Database.Open"); return; } if (is_Open ()) { // Close the existing connection. if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Database.Open: Disconnect"); Disconnect (); } // Load the Data_Port class. String type = configuration.Get_One (TYPE); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println (" Database.Open: " + TYPE + " = " + type); if (type == null) throw new Configuration_Exception ( ID + NL + "To Open a database a \"" + TYPE + "\" parameter must be in the Configuration." ); Data_Port = Load_Data_Port (type); // Open the Data_Port. Data_Port.Open (configuration); // Initialize identifier case sensitivity. Case_Sensitive_Default = Data_Port.Case_Sensitive_Identifiers (); if ((DEBUG & DEBUG_CONNECT) != 0) System.out.println ("<<< Database.Open"); } /** Closes the Database connection to the database server.

@see #Disconnect() */ public void Close () throws Database_Exception {Disconnect ();} /** Tests if a connection to the database server is open.

@return true if the Data_Port is open; false otherwise. */ public boolean is_Open () { return Data_Port == null ? false : Data_Port.is_Open (); } /** Gets the JDBC Connection object. Note: A Data_Port is not required to use a JDBC Connection.

@return A JDBC Connection object associated with the Data_Port backing the Database. This will be null if the Data_Port is closed or it does not use a JDBC Connection. */ public Connection Connection () { if (Data_Port != null) return Data_Port.Connection (); return null; } /** Adds a SQL_Listener to the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be added. If null, or the listener is already listed, nothing is done. @return This Data_Port, or null if the Data_Port is closed. @see SQL_Listener */ public Data_Port Add_SQL_Listener ( SQL_Listener listener ) { if (Data_Port != null) return Data_Port.Add_SQL_Listener (listener); return null; } /** Removes a SQL_Listener from the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be removed. @return true if the listener was removed; false if the listener was not listed or the the Data_Port is closed. @see SQL_Listener */ public boolean Remove_SQL_Listener ( SQL_Listener listener ) { if (Data_Port != null) return Data_Port.Remove_SQL_Listener (listener); return false; } /*------------------------------------------------------------------------------ Description: */ /** @see Data_Port#toString() */ public String toString () { return Data_Port == null ? ID : ID + NL + Data_Port.toString (); } /** @see Data_Port#Description() */ public String Description () { return Data_Port == null ? ID : ID + NL + Data_Port.Description (); } /** @see Data_Port#Contents(String, String) */ public String Contents ( String catalog, String table ) { return Data_Port == null ? ID : ID + NL + Data_Port.Contents (catalog, table); } /** @see Data_Port#Catalogs() */ public Vector Catalogs () throws Database_Exception { connect_check (); return Data_Port.Catalogs (); } /** @see Data_Port#Tables(String) */ public Vector Tables ( String catalog ) throws Database_Exception { connect_check (); return Data_Port.Tables (catalog); } /** @see Data_Port#Field_Names(String) */ public Vector Field_Names ( String table ) throws Database_Exception { connect_check (); return Data_Port.Field_Names (table); } /** @see Data_Port#Field_Types(String) */ public Vector Field_Types ( String table ) throws Database_Exception { connect_check (); return Data_Port.Field_Types (table); } /** @see Data_Port#Fields(String, String) */ public Vector Fields ( String table, String field_info ) throws Database_Exception { connect_check (); return Data_Port.Fields (table, field_info); } /** @see Data_Port#Keys(String) */ public Vector Keys ( String table ) throws Database_Exception { connect_check (); return Data_Port.Keys (table); } /*------------------------------------------------------------------------------ Query: */ /** @see Data_Port#Query(String, int) */ public Vector Query ( String SQL_Query, int limit ) throws Database_Exception { connect_check (); return Data_Port.Query (SQL_Query, limit); } /** @see Data_Port#Select(Vector, Vector, String, int) */ public Vector Select ( Vector tables, Vector fields, String conditions, int limit ) throws Database_Exception { connect_check (); return Data_Port.Select (tables, fields, conditions, limit); } /*------------------------------------------------------------------------------ Update: */ /** @see Data_Port#Update(String) */ public int Update ( String SQL_Update ) throws Database_Exception { connect_check (); return Data_Port.Update (SQL_Update); } /*.............................................................................. Catalogs: */ /** @see Data_Port#Create(String) */ public void Create ( String catalog ) throws Database_Exception { connect_check (); Data_Port.Create (catalog); } /** @see Data_Port#Delete(String) */ public void Delete ( String catalog ) throws Database_Exception { connect_check (); Data_Port.Delete (catalog); } /*.............................................................................. Tables: */ /** @see Data_Port#Create(String, Vector, Vector) */ public void Create ( String table, Vector fields, Vector types ) throws Database_Exception { connect_check (); Data_Port.Create (table, fields, types); } /** @see Data_Port#Delete(String, Vector) */ public void Delete ( String table, Vector fields ) throws Database_Exception { connect_check (); Data_Port.Delete (table, fields); } /** @see Data_Port#Rename(String, String) */ public void Rename ( String table, String name ) throws Database_Exception { connect_check (); Data_Port.Rename (table, name); } /** @see Data_Port#Rename(String, Vector, Vector) */ public void Rename ( String table, Vector fields, Vector names ) throws Database_Exception { connect_check (); Data_Port.Rename (table, fields, names); } /*.............................................................................. Fields: */ /** @see Data_Port#Insert(String, Vector, Vector) */ public int Insert ( String table, Vector fields, Vector values ) throws Database_Exception { connect_check (); return Data_Port.Insert (table, fields, values); } /** @see Data_Port#Update(String, Vector, Vector, String) */ public int Update ( String table, Vector fields, Vector values, String conditions ) throws Database_Exception { connect_check (); return Data_Port.Update (table, fields, values, conditions); } /** @see Data_Port#Delete(String, String) */ public int Delete ( String table, String conditions ) throws Database_Exception { connect_check (); return Data_Port.Delete (table, conditions); } /*.............................................................................. Utility: */ /** @see Data_Port#Table_Reference(String, String) */ public String Table_Reference ( String catalog, String table ) throws Database_Exception { connect_check (); return Data_Port.Table_Reference (catalog, table); } /** @see Data_Port#Catalog_Name(String) */ public String Catalog_Name ( String table_reference ) throws Database_Exception { connect_check (); return Data_Port.Catalog_Name (table_reference); } /** @see Data_Port#Database_Catalog_Name(String) */ public String Database_Catalog_Name ( String catalog ) throws Database_Exception { connect_check (); return Data_Port.Database_Catalog_Name (catalog); } /** @see Data_Port#Table_Name(String) */ public String Table_Name ( String table_reference ) throws Database_Exception { connect_check (); return Data_Port.Table_Name (table_reference); } /** @see Data_Port#Database_Table_Name(String) */ public String Database_Table_Name ( String table ) throws Database_Exception { connect_check (); return Data_Port.Database_Table_Name (table); } /** @see Data_Port#Table_Reference_Component_Delimiter() */ public String Table_Reference_Component_Delimiter () throws Database_Exception { connect_check (); return Data_Port.Table_Reference_Component_Delimiter (); } /** @see Data_Port#Case_Sensitive_Identifiers() */ public boolean Case_Sensitive_Identifiers () throws Database_Exception { connect_check (); return Data_Port.Case_Sensitive_Identifiers (); } /** @see Data_Port#Case_Sensitive_Identifiers(boolean) */ public void Case_Sensitive_Identifiers ( boolean case_sensitive ) throws Database_Exception { connect_check (); Data_Port.Case_Sensitive_Identifiers (case_sensitive); } /*============================================================================== Convenience methods */ /*.............................................................................. Select: */ /** Selects fields conditionally from a list of tables with no limit on the number of records returned.

@see #Select(Vector, Vector, String, int) */ public Vector Select (Vector tables, Vector fields, String conditional) throws Database_Exception {return Select (tables, fields, conditional, -1);} /** Selects fields conditionally from a single table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (String table, Vector fields, String conditional) throws Database_Exception { Vector tables = new Vector (1); tables.add (table); return Select (tables, fields, conditional, -1); } /** Selects the entire default table.

@see #Select(Vector, Vector, String, int) */ public Vector Select () throws Database_Exception { return Select ((Vector)null, null, null, -1); } /** Selects an entire single table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (String table) throws Database_Exception { return Select (table, null, null); } /** Selects unconditionally from the fields of a single table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (String table, Vector fields) throws Database_Exception { return Select (table, fields, null); } /** Selects all fields conditionally from a single table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (String table, String conditional) throws Database_Exception { return Select (table, null, conditional); } /** Selects fields unconditionally from the default table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (Vector fields) throws Database_Exception { return Select ((Vector)null, fields, null, -1); } /** Selects fields conditionally from the default table.

@see #Select(Vector, Vector, String, int) */ public Vector Select (Vector fields, String conditional) throws Database_Exception { return Select ((Vector)null, fields, conditional, -1); } /** Unlimited Query.

@see #Query(String, int) */ public Vector Query ( String SQL_Query ) throws Database_Exception { connect_check (); return Data_Port.Query (SQL_Query, -1); } /*.............................................................................. Insert: */ /** Inserts the set of records into a table.

The set of records is like that returned from the {@link #Select(Vector, Vector, String, int) Select} or {@link #Query(String, int) Query} methods: a Vector of record Vectors, where the first Vector contains the String names of the fields corresponding to the data values in the subsequent Vectors. The data records do not necessarily have to contain Strings, as long as each object's toString method produces a valid representation of the field value. Though all the records must be the same size, not all fields in the table need to be included; the database is expected to provide the default for missing fields.

An entire table can be copied from an old_table into a new_table using:

database.Insert (new_table, database.Select (old_table))

@param table The name of the table in which to insert the records. This may be null to use the default table. @param records The field names record followed by a set of data records. @return The number of records inserted. @throws Database_Exception If an insertion failed. @see #Insert(String, Vector, Vector) */ public int Insert ( String table, Vector records ) throws Database_Exception { if (records == null) return 0; int count; for (count = 1; count < records.size (); count++) Insert (table, (Vector)records.get (0), (Vector)records.get (count)); return --count; } /*============================================================================== Utility methods */ /** Constructs a Hashtable from a Vector of keys and a corresponding Vector of values.

@param keys The Vector of objects to be used as the Hashtable keys. @param values The Vector of objects to be used as the Hashtable values. @return A Hashtable of key/value pairs. This will be null if either the keys or values Vectors are null. @throws Database_Exception If the keys and values Vectors are not the same size, there is a duplicate (equals) key object, or a key or value is null. @see Hashtable#put(Object, Object) */ public static Hashtable Hash_Map ( Vector keys, Vector values ) throws Database_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Database.Hash_Map:" + NL + " keys - " + keys + NL + " values - " + values); if (keys == null || values == null) return null; if (keys.size () != values.size ()) throw new Database_Exception ( ID + NL + "Hash_Map was given " + keys.size () + " key" + ((keys.size () == 1) ? " " : "s ") + "but " + values.size () + " value" + ((values.size () == 1) ? "." : "s.") ); Hashtable keys_map = new Hashtable (); Iterator key_list = keys.iterator (), value_list = values.iterator (); while (key_list.hasNext ()) { String key = (String)key_list.next (); if (keys_map.put (key, value_list.next ()) != null) throw new Database_Exception ( ID + NL + "Invalid hash map entry for key \"" + key + "\"." ); } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Database.Hash_Map: " + keys_map); return keys_map; } /** Tests if two strings match.

This is a helper function used by the Matches methods.

@param string_1 A String to be compared. @param string_2 The other String for the comparison. @param case_sensitive If true, the string comparison will be case sensitive; otherwise the comparison will ignore case. @return true if the strings match, false otherwise. If both strings are null they match. If only one string is null, they do not match. @see #Matches(String, String) */ public static boolean Matches ( String string_1, String string_2, boolean case_sensitive ) { if (string_1 == null && string_2 == null) return true; if (string_1 == null || string_2 == null) return false; return case_sensitive ? string_1.equals (string_2) : string_1.equalsIgnoreCase (string_2); } /** Tests if two strings match.

The {@link #Matches(String, String, boolean) Matches} function will be used with case sensitivity determined by the {@link #Case_Sensitive_Identifiers() Data_Port identifier case sensitivity} or the {@link #Case_Sensitive_Default} value if the Database is not bound to a Data_Port.

@param string_1 A String to be compared. @param string_2 The other String for the comparison. @return true if the strings match, false otherwise. If both strings are null they match. @see #Matches(String, String, boolean) */ public boolean Matches ( String string_1, String string_2 ) { boolean case_sensitive = Case_Sensitive_Default; if (Data_Port != null) { try {case_sensitive = Data_Port.Case_Sensitive_Identifiers ();} catch (Database_Exception exception) {/* Can't happen */} } return Matches (string_1, string_2, case_sensitive); } /** Tests if a string matches any element of a Vector.

This is a helper function used by the corresponding Matches method.

@param vector The Vector to search for a matching String. Each element of the Vector is cast to a String. If null, false is returned. @param string The String to be compared. May be null, in which case the vector must contain a null element for true to be returned. @param case_sensitive If true, the string comparison will be case sensitive; otherwise the comparison will ignore case. @return true if the string matches any element of the vector, false otherwise. @see #Matches(String, String, boolean) */ public static boolean Matches ( Vector vector, String string, boolean case_sensitive ) { if (vector == null) return false; for (int index = 0, size = vector.size (); index < size; index++) if (Matches (string, (String)vector.get (index), case_sensitive)) return true; return false; } /** Tests if a string matches any element of a Vector.

The {@link #Matches(Vector, String, boolean) Matches} function will be used with case sensitivity determined by the {@link #Case_Sensitive_Identifiers() Data_Port identifier case sensitivity} or the {@link #Case_Sensitive_Default} value if the Database is not bound to a Data_Port.

@param vector The Vector to search for a matching String. Each element of the Vector is cast to a String. If null, false is returned. @param string The String to be compared. May be null, in which case the vector must contain a null element for true to be returned. @return true if the string matches any element of the vector, false otherwise. */ public boolean Matches ( Vector vector, String string ) { boolean case_sensitive = Case_Sensitive_Default; if (Data_Port != null) { try {case_sensitive = Data_Port.Case_Sensitive_Identifiers ();} catch (Database_Exception exception) {/* Can't happen */} } return Matches (vector, string, case_sensitive); } /** Gets the index of the string that matches an element of a Vector.

This is a helper function used by the corresponding Index method.

@param vector The Vector to search for a matching String. Each element of the Vector is cast to a String. If null, -1 is returned. @param string The String to be compared. May be null, in which case the index of the first null element of the vector, if any, will be returned. @return The index of the first element of the vector that matches the string, or -1 if no match is found. @see #Matches(String, String, boolean) */ public static int Index ( Vector vector, String string, boolean case_sensitive ) { if (vector == null) return -1; for (int index = 0, size = vector.size (); index < size; index++) if (Matches (string, (String)vector.get (index), case_sensitive)) return index; return -1; } /** Gets the index of the string that matches an element of a Vector.

The {@link #Index(Vector, String, boolean) Index} function will be used with case sensitivity determined by the {@link #Case_Sensitive_Identifiers() Data_Port identifier case sensitivity} or the {@link #Case_Sensitive_Default} value if the Database is not bound to a Data_Port.

@param vector The Vector to search for a matching String. Each element of the Vector is cast to a String. If null, -1 is returned. @param string The String to be compared. May be null, in which case the index of the first null element of the vector, if any, will be returned. @return The index of the first element of the vector that matches the string, or -1 if no match is found. */ public int Index ( Vector vector, String string ) { boolean case_sensitive = Case_Sensitive_Default; if (Data_Port != null) { try {case_sensitive = Data_Port.Case_Sensitive_Identifiers ();} catch (Database_Exception exception) {/* Can't happen */} } return Index (vector, string, case_sensitive); } /** Gets the index of the string that matches an element of an array.

This is a helper function used by the corresponding Index method.

@param array The array of Strings to search for a matching String. If null, -1 is returned. @param string The String to be compared. May be null, in which case the index of the first null element of the array, if any, will be returned. @return The index of the first element of the array that matches the string, or -1 if no match is found. @see #Matches(String, String, boolean) */ public static int Index ( String[] array, String string, boolean case_sensitive ) { if (array == null) return -1; for (int index = 0; index < array.length; index++) if (Matches (string, array[index], case_sensitive)) return index; return -1; } /** Gets the index of the string that matches an element of an array.

The {@link #Index(String[], String, boolean) Index} function will be used with case sensitivity determined by the {@link #Case_Sensitive_Identifiers() Data_Port identifier case sensitivity} or the {@link #Case_Sensitive_Default} value if the Database is not bound to a Data_Port.

@param array The array of Strings to search for a matching String. If null, -1 is returned. @param string The String to be compared. May be null, in which case the index of the first null element of the array, if any, will be returned. @return The index of the first element of the array that matches the string, or -1 if no match is found. */ public int Index ( String[] array, String string ) { boolean case_sensitive = Case_Sensitive_Default; if (Data_Port != null) { try {case_sensitive = Data_Port.Case_Sensitive_Identifiers ();} catch (Database_Exception exception) {/* Can't happen */} } return Index (array, string, case_sensitive); } /** Substitutes special characters for escape sequences.

The following escape sequences, and their corresponding special characters, are recognized:

\b - Backspace (BS)
\t - Horizontal tab (HT)
\n - Newline (NL)
\f - Form feed (FF)
\r - Carriage return (CR)
\X - The character X
\0nnn - The character having the octal value nnn (0 <= nnn <= 377)
The escape sequences will be substituted for their corresponding special characters. All backslash characters, except those that are themselves escaped, will be removed.

@param string The String to be un-escaped. @return The un-escaped String. */ public static String Escape_to_Special ( String string ) { if (string == null) return null; StringBuffer mung = new StringBuffer (string); int index; for (index = 0; index < mung.length (); index++) { if (mung.charAt (index) == '\\') { // Delete the backslash. mung.deleteCharAt (index); // Check the escaped character. switch (mung.charAt (index)) { case 'b': // BS mung.setCharAt (index, '\b'); break; case 't': // HT mung.setCharAt (index, '\t'); break; case 'n': // NL mung.setCharAt (index, '\n'); break; case 'f': // FF mung.setCharAt (index, '\f'); break; case 'r': // CR mung.setCharAt (index, '\r'); break; case '0': // Octal value. char character, new_character = 0; int end_index; for (end_index = index + 1; end_index < mung.length () && (end_index - index) < 4; // No more that four digits. end_index++) { // Only octal digits are acceptable. if ((character = mung.charAt (end_index)) > '7' || character < '0') break; new_character *= 8; new_character += character - 48; } if (new_character < 256) { mung.setCharAt (index, new_character); mung.delete (index + 1, end_index); } } } } return mung.toString (); } /** Substitutes escape sequences for special characters in a String.

All control characters - less than ' ' (32), plus single quote (39), double quote (34), backslash (92) and DEL (127) - will be substituted with escape sequences:

\0 - for an ASCII 0 (NUL, 0) character
\b - for backspace (BS, 8)
\t - for horizontal tab (HT, 9)
\n - for newline (NL, 10)
\f - for form feed (FF, 12)
\r - for carriage return (CR, 13)
\' - for a single quote (', 39)
\" - for a double quote (", 34)
\\ - for backslash (\, 92)
\0nnn - for all other special characters where nnn is the octal value of the character.

Escape sequences in the string will be recognized and left unchanged.

@param string The String to be escaped. @return The escaped String. */ public static String Special_to_Escape ( String string ) { if (string == null) return null; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Database.Special_to_Escape: " + string); StringBuffer mung = new StringBuffer (string); char character; String escape; int index = 0; while (index < mung.length ()) { character = mung.charAt (index); if (character < 32 || character == '"' || character == '\'' || character >= 127) { // Check the special character. switch (character) { case '\b': // BS escape = "b"; break; case '\t': // HT escape = "t"; break; case '\n': // NL escape = "n"; break; case '\f': // FF escape = "f"; break; case '\r': // CR escape = "r"; break; case '\'': // CR escape = "'"; break; case '"': // CR escape = "\""; break; default: // Octal value. escape = "0" + Integer.toString ((int)character, 8); } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.print ("|\\" + escape + "|"); mung.setCharAt (index++, '\\'); mung.insert (index, escape); index += escape.length (); } else if (character == '\\') { // Backslash if (++index < mung.length () && (character = mung.charAt (index)) == '\\' || character == 'b' || character == 't' || character == 'n' || character == 'f' || character == 'r' || character == '\'' || character == '"' || character == '0') { // Escape sequence if ((DEBUG & DEBUG_UTILITY) != 0) System.out.print ("\\" + character); index++; continue; } // Escape the backslash if ((DEBUG & DEBUG_UTILITY) != 0) System.out.print ("|\\\\|"); mung.insert (index++, '\\'); } else { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.print (character); index++; } } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (NL +"<<< Database.Special_to_Escape: " + mung.toString ()); return mung.toString (); } /** Filters a data value to protect special characters.

If the data type is non-numeric it is {@link #Special_to_Escape(String) Special_to_Escape} filtered and enclosed in single quote (') characters. A numeric data type is identified by containing in its name one of these (case insensitive) substrings:

  • INT
  • REAL
  • FLOAT
  • DOUBLE
The data value of a numeric data type is not altered.

@param value A String representing a data value. @param type The JDBC name of the value's data type. @return The possibly altered value string. */ public static String Value_Syntax ( String value, String type ) { if (type == null) type = ""; if (value == null) value = NULL_VALUE; type = type.toUpperCase (); if (type.indexOf (INT_TYPE) >= 0 || type.indexOf (REAL_TYPE) >= 0 || type.indexOf (FLOAT_TYPE) >= 0 || type.indexOf (DOUBLE_TYPE) >= 0 || value.equals (NULL_VALUE)) { if ((DEBUG & DEBUG_VALUE) != 0) System.out.println (">-< Database.Value_Syntax: " + value); return value; } if ((DEBUG & DEBUG_VALUE) != 0) System.out.println (">-< Database.Value_Syntax: '" + value + "'"); return "'" + Special_to_Escape (value) + "'"; } private static final String INT_TYPE = "INT", REAL_TYPE = "REAL", FLOAT_TYPE = "FLOAT", DOUBLE_TYPE = "DOUBLE"; /*============================================================================== Private helper methods */ private void connect_check () throws Database_Exception { if (Data_Port == null) throw new Database_Exception ( ID + NL + "The database is not connected." ); } } // End of class pirl-2.3.8/PIRL/Database/Database_View.java0000644000175000017500000014060511742733571020177 0ustar mathieumathieu/* Database_View PIRL CVS ID: Database_View.java,v 1.13 2012/04/16 06:08:57 castalia Exp Copyright (C) 2002-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.PVL.*; import PIRL.Viewers.*; import PIRL.Strings.Words; import PIRL.Configuration.Configuration; import javax.swing.*; import javax.swing.border.*; import javax.swing.tree.*; import javax.swing.table.*; import javax.swing.event.TableModelListener; import javax.swing.event.TableModelEvent; import java.awt.*; import java.awt.event.*; import java.util.Vector; import java.util.Iterator; import java.util.Enumeration; import java.util.TreeSet; import java.lang.StringBuffer; import java.net.URL; /** Database_View provides a View of a Database.

Database management capabilities are provided.

@author Bradford Castalia, UA/PIRL @version 1.13 */ public class Database_View extends JFrame { private static final String ID = "PIRL.Database.Database_View (1.13 2012/04/16 06:08:57)"; // Configurable parameters: private static final int DEFAULT_VIEW_WIDTH = 500, DEFAULT_SELECTION_PANEL_HEIGHT = 350, DEFAULT_DATA_TABLE_HEIGHT = 150, LIST_WIDTH = 200, LIST_HEIGHT = 150, DEFAULT_LIMIT = 50; // Database. private Database The_Database = null; // Configuration. private Configuration The_Configuration = null; private Parameter_View Configuration_View = null; private Dimension Configuration_View_Size = new Dimension (250, 300); private View_Locator Configuration_View_Locator = new View_Locator (); // GUI elements. private JTabbedPane Modes_Panes; private static Color Normal_Background_Color = null; // SELECT. private int SELECT_PANE; private static ImageIcon SELECT_ICON = null; private static final String SELECT_ICON_NAME = "Export_24.gif"; private JPanel Selection_Panels; private JTree Fields_Available_Tree, Fields_Chosen_Tree; private DefaultTreeModel Fields_Available_Tree_Model = new DefaultTreeModel (new DefaultMutableTreeNode ("Source Fields")), Fields_Chosen_Tree_Model; private JTextArea Conditions; // JSpinner is new with Java 1.4; use it when 1.4 is ubiquitous. //private JSpinner Limit; private JTextField Limit; private JButton Include_Button, Exclude_Button, Join_Button, Reset_Button, Update_Button, SQL_Button, Select_Button; private static ImageIcon INCLUDE_ICON; private static final String INCLUDE_ICON_NAME = "Arrow_Right16.gif"; private static ImageIcon EXCLUDE_ICON; private static final String EXCLUDE_ICON_NAME = "Arrow_Left16.gif"; private Data_Table Select_Table; private JLabel Select_Records; private String Current_SQL_Command = ""; private View_Locator SQL_Locator = new View_Locator (); private static int UPDATE_DISABLED = 0, UPDATE_PENDING = 1, UPDATE_ENABLED = -1; private int Update_Enabled = UPDATE_DISABLED; private static final String UPDATE_ENABLED_PARAMETER = "Update_Enabled"; // INSERT. private int INSERT_PANE; private static ImageIcon INSERT_ICON; private static final String INSERT_ICON_NAME = "Import_24.gif"; private static final Color INSERT_COLOR = new Color ((float)0.85, (float)0.75, (float)0.85); private JPanel Insertion_Panels; private JTree Insert_Fields_Tree; private DefaultTreeModel Insert_Fields_Tree_Model = new DefaultTreeModel (new DefaultMutableTreeNode ("Insert Records")); private JButton Insert_into_Button, Clear_Button, New_Button, Insert_Button; private static ImageIcon INSERTION_ICON; private static final String INSERTION_ICON_NAME = "Arrow_Down16.gif"; private Data_Table Insert_Table; private JLabel Insertion_Table; private boolean Insert_Enabled = false; private static final String INSERT_ENABLED_PARAMETER = "Insert_Enabled"; // Miscellaneous. private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_QUERY = 1 << 1, DEBUG_UI_SELECTION = 1 << 2, DEBUG_SELECT_TABLE = 1 << 3, DEBUG_INSERT_TABLE = 1 << 4, DEBUG_UI_LAYOUT = 1 << 5, DEBUG_SQL = 1 << 6, DEBUG_CONFIGURATION = 1 << 7, DEBUG_UTILITY = 1 << 8, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ public Database_View ( Database database ) throws Database_Exception { if ((DEBUG & DEBUG_SETUP) != 0) System.out.println (">>> Database_View"); // Ensure that the database is open for access. if (database == null || ! database.is_Open ()) { // Open the default database. try {database.Open (null);} catch (Exception exception) { Dialog_Box.Error ("Unable to Open the default database." + NL + NL + exception.getMessage ()); throw new Database_Exception (ID + NL + exception.getMessage ()); } } The_Database = database; // Setup the configuration. Configure (); // Load the icons. Load_Icons (this); /* // Build the menu bar. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Menu_Bar"); setJMenuBar (Menu_Bar ()); */ // Setup the Mode tabbed panes. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Modes_Panes"); Modes_Panes = Modes_Panes (); Modes_Panes.setEnabledAt (INSERT_PANE, Insert_Enabled); // Put the modes panes in the ContentPane. getContentPane().add (Modes_Panes); // Disconnect on close. addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent event) { try {The_Database.Disconnect ();} catch (Database_Exception exception) {Dialog_Box.Error (exception.getMessage ());} }}); pack (); if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("<<< Database_View"); } private Database_View () throws Exception { throw new Exception ( getClass().getName() + ": Hey! You shouldn't be using the default constructor." ); } /*------------------------------------------------------------------------------ Configure */ private void Configure () throws Database_Exception { if ((DEBUG & DEBUG_CONFIGURATION) != 0) System.out.println (">>> Database_View.Configure"); The_Configuration = The_Database.Data_Port ().Configuration (); setTitle (The_Configuration.Name () + " Database at " + The_Configuration.Get_One (Configuration.HOST)); boolean case_sensitive = The_Configuration.Case_Sensitive (false); // Configuration flags. Update_Enabled = Data_View.get_flag (The_Configuration, UPDATE_ENABLED_PARAMETER, Update_Enabled != UPDATE_DISABLED) ? UPDATE_ENABLED : UPDATE_DISABLED; Insert_Enabled = Data_View.get_flag (The_Configuration, INSERT_ENABLED_PARAMETER, Insert_Enabled); The_Configuration.Case_Sensitive (case_sensitive); } /*============================================================================== Menus */ private JMenuBar Menu_Bar () { JMenuBar menu_bar = new JMenuBar (); JMenu view = new JMenu ("View"); JMenuItem config = new JMenuItem ("Configuration"); config.setMnemonic ('U'); config.setAccelerator (KeyStroke.getKeyStroke ('U', Event.CTRL_MASK)); config.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {View_Configuration ();}}); view.add (config); menu_bar.add (view); return menu_bar; } /*============================================================================== Menu Actions */ // VIEW the Configuration. private void View_Configuration () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> View_Configuration"); if (Configuration_View == null) { Configuration_View = new Parameter_View ( The_Configuration.Name () + " Database", The_Configuration ); Configuration_View .addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent event) {Configuration_View = null;}}); // Locate the Configuration_View on the screen. Configuration_View.setSize (Configuration_View_Size); Configuration_View_Locator .Offsets (0, 0) .Vertical (View_Locator.TOP | View_Locator.INWARD); if (Configuration_View_Size.width > getX ()) Configuration_View_Locator .Horizontal (View_Locator.RIGHT | View_Locator.OUTWARD); else Configuration_View_Locator .Horizontal (View_Locator.LEFT | View_Locator.OUTWARD); Configuration_View_Locator.Relocate (Configuration_View, this); Configuration_View.setVisible (true); } Configuration_View.toFront (); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< View_Configuration"); } /*============================================================================== Modes Panels */ private JTabbedPane Modes_Panes () { JTabbedPane modes_panes = new JTabbedPane (); /*.............................................................................. SELECT */ // Setup the selection panels. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Selection_Panels"); Selection_Panels = Selection_Panels (); Selection_Panels.setPreferredSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_SELECTION_PANEL_HEIGHT)); //this.getRootPane ().setDefaultButton (Select_Button); // Assemble the field lists. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Assemble_Fields"); Assemble_Fields (); Include_Default_Selections (); Fields_Available_Tree.expandPath (new TreePath (Fields_Available_Tree_Model.getRoot ())); Fields_Chosen_Tree.expandPath (new TreePath (Fields_Chosen_Tree_Model.getRoot ())); // Prepare the data table panel. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Data_Table"); Select_Table = new Data_Table (); Select_Table.Table_Model ().addTableModelListener (new Select_Table_Changed ()); Select_Table.setPreferredSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_DATA_TABLE_HEIGHT)); // Put the selection panel and data table panel in a split panel. JSplitPane split_pane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, Selection_Panels, Select_Table); split_pane.setOneTouchExpandable (true); modes_panes.addTab ("Select", SELECT_ICON, split_pane, "Select data"); SELECT_PANE = 0; /*.............................................................................. INSERT */ // Setup the insertion panels. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Insertion_Panels"); Insertion_Panels = Insertion_Panels (); Insertion_Panels.setPreferredSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_SELECTION_PANEL_HEIGHT)); Insert_Fields_Tree.expandPath (new TreePath (Insert_Fields_Tree_Model.getRoot ())); // Prepare the data table panel. if ((DEBUG & DEBUG_SETUP) != 0) System.out.println ("--- Insert_Table"); Insert_Table = new Data_Table (); Insert_Table.setPreferredSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_DATA_TABLE_HEIGHT)); Insert_Table.Record_Numbers_Color (INSERT_COLOR); // Put the insertion panel and data table panel in a split panel. split_pane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, Insertion_Panels, Insert_Table); split_pane.setOneTouchExpandable (true); modes_panes.addTab ("Insert", INSERT_ICON, split_pane, "Insert data"); INSERT_PANE = 1; modes_panes.setSelectedIndex (SELECT_PANE); return modes_panes; } /*============================================================================== SELECT */ private JPanel Selection_Panels () { JPanel panels = new JPanel (new GridBagLayout ()); /*.............................................................................. Fields panel */ JPanel fields_panel = new JPanel (new GridBagLayout ()); fields_panel.setBorder (new TitledBorder (new EtchedBorder (), " Fields ", TitledBorder.LEFT, TitledBorder.TOP)); GridBagConstraints location = new GridBagConstraints (); //- Row 0 // Col 0: glue location.insets = new Insets (0, 0, 0, 0); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; fields_panel.add (Box.createHorizontalGlue (), location); // Col 1: Available label location.insets = new Insets (0, 0, 0, 5); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; fields_panel.add (new JLabel ("Available"), location); // Col 2: Include button Include_Button = new JButton ("Include", INCLUDE_ICON); Include_Button.setHorizontalTextPosition (SwingConstants.LEADING); Include_Button.setMargin (new Insets (0, 3, 0, 0)); Include_Button.setToolTipText ("Add to fields selections"); Include_Button.setMnemonic ('I'); Include_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Include_Fields ();}}); location.insets = new Insets (0, 0, 0, 0); fields_panel.add (Include_Button, location); // Col 3: strut (between lists) location.anchor = GridBagConstraints.WEST; fields_panel.add (Box.createHorizontalStrut (5), location); // Col 4: Exclude button Exclude_Button = new JButton ("Exclude", EXCLUDE_ICON); Exclude_Button.setMargin (new Insets (0, 0, 0, 3)); Exclude_Button.setToolTipText ("Remove from fields selections"); Exclude_Button.setMnemonic ('E'); Exclude_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Exclude_Fields ();}}); fields_panel.add (Exclude_Button, location); // Col 5: Chosen label location.insets = new Insets (0, 5, 0, 5); location.gridwidth = GridBagConstraints.REMAINDER; fields_panel.add (new JLabel ("Chosen"), location); //- Row 1 // Col 0-2: Source fields tree. Fields_Available_Tree = new JTree (Fields_Available_Tree_Model); ((DefaultTreeCellRenderer)Fields_Available_Tree.getCellRenderer ()) .setLeafIcon (null); Fields_Available_Tree.setRootVisible (false); Fields_Available_Tree.setShowsRootHandles (true); //Fields_Available_Tree.setDragEnabled (true); // 1.4 ToolTipManager.sharedInstance().registerComponent(Fields_Available_Tree); ((JComponent)Fields_Available_Tree.getCellRenderer ()) .setToolTipText ("Fields in the database"); JScrollPane source_fields = new JScrollPane (Fields_Available_Tree); source_fields.setMinimumSize (new Dimension (100, 100)); source_fields.setPreferredSize (new Dimension (LIST_WIDTH, LIST_HEIGHT)); location.insets = new Insets (0, 5, 5, 0); location.gridwidth = 3; location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; fields_panel.add (source_fields, location); // Col 3: strut (between lists) location.insets = new Insets (0, 0, 5, 0); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; fields_panel.add (Box.createHorizontalStrut (10), location); // Col 4-: Selected fields tree. Fields_Chosen_Tree_Model = new DefaultTreeModel (new DefaultMutableTreeNode ("Selected Fields")); Fields_Chosen_Tree = new JTree (Fields_Chosen_Tree_Model); ((DefaultTreeCellRenderer)Fields_Chosen_Tree.getCellRenderer ()) .setLeafIcon (null); Fields_Chosen_Tree.setRootVisible (false); Fields_Chosen_Tree.setShowsRootHandles (true); //Fields_Chosen_Tree.setDragEnabled (true); // 1.4 ToolTipManager.sharedInstance().registerComponent(Fields_Chosen_Tree); ((JComponent)Fields_Chosen_Tree.getCellRenderer ()) .setToolTipText ("Fields to be selected"); JScrollPane selected_fields = new JScrollPane (Fields_Chosen_Tree); selected_fields.setMinimumSize (new Dimension (100, 100)); selected_fields.setPreferredSize (new Dimension (LIST_WIDTH, LIST_HEIGHT)); location.insets = new Insets (0, 0, 5, 5); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; fields_panel.add (selected_fields, location); location.insets = new Insets (5, 5, 5, 5); panels.add (fields_panel, location); /*.............................................................................. Conditions panel */ JPanel conditions_panel = new JPanel (new GridBagLayout ()); conditions_panel.setBorder (new TitledBorder (new EtchedBorder (), " Conditions ", TitledBorder.LEFT, TitledBorder.TOP)); //- Row 0: Conditions text. Conditions = new JTextArea (4, 32); Conditions.setLineWrap (true); Conditions.setWrapStyleWord (true); Conditions.setToolTipText ("Field selection conditions"); JScrollPane conditions_pane = new JScrollPane (Conditions); conditions_pane.setMinimumSize (new Dimension (200, 50)); location.insets = new Insets (0, 5, 0, 5); conditions_panel.add (conditions_pane, location); //- Row 1 // Col 0: Joins button. Join_Button = new JButton ("Join ..."); Join_Button.setToolTipText ("Join fields of tables"); Join_Button.setMnemonic ('J'); Join_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Join ();}}); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; //conditions_panel.add (Join_Button, location); // Col 1: Limit field. Box limits = Box.createHorizontalBox (); JLabel limit_label = new JLabel ("Limit:"); limit_label.setToolTipText ("Maximum records to get"); limits.add (limit_label); limits.add (Box.createHorizontalStrut (5)); /* JSpinner is new with Java 1.4; use it when 1.4 is ubiquitous. Limit = new JSpinner (new SpinnerNumberModel (new Integer (DEFAULT_LIMIT), // Initial value new Integer (0), // Minimum null, // Maximum (null == unlimited) new Integer (1))); // Step size */ Limit = new JTextField (6); Limit.setHorizontalAlignment (JTextField.RIGHT); Limit.setText (String.valueOf (DEFAULT_LIMIT)); Limit.setMinimumSize (new Dimension (75, 25)); // How is a JSpinner ToolTip set? Limit.setToolTipText ("Maximum number of records"); limits.add (Limit); location.insets = new Insets (5, 0, 5, 5); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; conditions_panel.add (limits, location); location.insets = new Insets (3, 5, 3, 5); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panels.add (conditions_panel, location); /*.............................................................................. Buttons panel */ // Col 0: Reset button JPanel buttons_panel = new JPanel (new GridBagLayout ()); Reset_Button = new JButton ("Reset"); Reset_Button.setToolTipText ("Reset selections and conditions"); Reset_Button.setMnemonic ('R'); Reset_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Reset_Selections ();}}); location.insets = new Insets (0, 0, 0, 0); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; buttons_panel.add (Reset_Button, location); // Col 1: glue location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; buttons_panel.add (Box.createHorizontalGlue (), location); // Col 2: Update button Update_Button = new JButton ("Update"); Update_Button.setToolTipText ("Update edited data values"); Update_Button.setMnemonic ('U'); Update_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Update ();}}); location.fill = GridBagConstraints.NONE; location.weightx = 0.0; buttons_panel.add (Update_Button, location); Update_Button.setEnabled (Update_Enabled != UPDATE_DISABLED); Normal_Background_Color = Update_Button.getBackground (); // Col 1: glue location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; buttons_panel.add (Box.createHorizontalGlue (), location); // Col 3: SQL button SQL_Button = new JButton ("SQL ..."); SQL_Button.setToolTipText ("Compose custom SQL syntax"); SQL_Button.setMnemonic ('Q'); SQL_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {SQL ();}}); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; buttons_panel.add (SQL_Button, location); // Col 4: strut fields_panel.add (Box.createHorizontalStrut (5), location); // Col 5: Select button Select_Button = new JButton ("Select"); Select_Button.setToolTipText ("Query the database"); Select_Button.setMnemonic ('S'); Select_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Select ();}}); location.gridwidth = GridBagConstraints.REMAINDER; buttons_panel.add (Select_Button, location); location.insets = new Insets (0, 7, 5, 7); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 0.0; location.weighty = 0.0; panels.add (buttons_panel, location); return panels; } /*------------------------------------------------------------------------------ Fields */ private void Assemble_Fields () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Assemble_Fields"); Fields_Available_Tree.clearSelection (); DefaultMutableTreeNode selects = (DefaultMutableTreeNode)Fields_Available_Tree_Model.getRoot (), inserts = (DefaultMutableTreeNode)Insert_Fields_Tree_Model.getRoot (), selects_catalog, inserts_catalog, selects_table, inserts_table; selects.removeAllChildren (); inserts.removeAllChildren (); TreeSet catalogs; try {catalogs = new TreeSet (The_Database.Catalogs ());} catch (Database_Exception exception) { Dialog_Box.Warning ("Unable to access the database catalogs." + NL + NL + ID + NL + exception.getMessage ()); Fields_Available_Tree_Model.reload (); return; } if (catalogs.isEmpty ()) { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Assemble_Fields: no catalogs"); return; } if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" catalogs: " + catalogs); Iterator catalogs_list = catalogs.iterator (); while (catalogs_list.hasNext ()) { selects_catalog = new DefaultMutableTreeNode (catalogs_list.next ()); inserts_catalog = new DefaultMutableTreeNode (Name (selects_catalog)); TreeSet tables; try {tables = new TreeSet (The_Database.Tables (Name (selects_catalog)));} catch (Database_Exception exception) {/* Can't access that catalog */ continue;} if (tables.isEmpty ()) // No accessable tables. continue; if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" " + Name (selects_catalog) + " tables: " + tables); Iterator tables_list = tables.iterator (); while (tables_list.hasNext ()) { selects_table = new DefaultMutableTreeNode (tables_list.next ()); inserts_table = new DefaultMutableTreeNode (Name (selects_table)); Vector fields; try {fields = The_Database.Field_Names (The_Database.Table_Reference (Name (selects_catalog), Name (selects_table)));} catch (Database_Exception exception) {/* Can't access that table */ continue;} if (fields.isEmpty ()) // No accessable fields. continue; selects_catalog.add (selects_table); inserts_catalog.add (inserts_table); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" " + Name (selects_catalog) + "." + Name (selects_table) + " fields: " + fields); Iterator fields_list = fields.listIterator (); String field; while (fields_list.hasNext ()) { field = (String)fields_list.next (); selects_table.add (new DefaultMutableTreeNode (field)); inserts_table.add (new DefaultMutableTreeNode (field)); } } if (selects_catalog.getChildCount () == 0) // Empty catalog. continue; selects.add (selects_catalog); inserts.add (inserts_catalog); } Fields_Available_Tree_Model.reload (); Insert_Fields_Tree_Model.reload (); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Assemble_Fields"); } private void Include_Default_Selections () { DefaultMutableTreeNode root = (DefaultMutableTreeNode)Fields_Available_Tree_Model.getRoot (), catalog, table, field; TreePath selection = new TreePath (root); catalog = Child (root, The_Configuration.Get_Linked_One (Database.CATALOG)); try { if (catalog != null && The_Database.Catalogs ().contains (Name (catalog))) { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" Initial catalog selected: " + Name (catalog)); selection = selection.pathByAddingChild (catalog); table = Child (catalog, The_Database.Table_Name (The_Configuration.Get_Linked_One (Name (catalog) + The_Configuration.Path_Delimiter () + Database.TABLE))); if (table != null && The_Database.Tables (Name (catalog)).contains (Name (table))) { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" Initial table selected: " + Name (table)); selection = selection.pathByAddingChild (table); Vector default_fields = The_Configuration.Get_Linked (Name (catalog) + The_Configuration.Path_Delimiter () + The_Database.Table_Name (Name (table)) + The_Configuration.Path_Delimiter () + Database.FIELDS); if (default_fields.isEmpty ()) Fields_Available_Tree.addSelectionPath (selection); else { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" Initial fields selected: " + default_fields); Iterator fields_list = default_fields.iterator (); while (fields_list.hasNext ()) if ((field = Child (table, (String)fields_list.next ())) != null) Fields_Available_Tree.addSelectionPath (selection.pathByAddingChild (field)); } Include_Fields (); } } } catch (Database_Exception exception) {Dialog_Box.Error (exception.getMessage ()); return;} } /*============================================================================== Selection Actions */ // INCLUDE fields from Available to Chosen. private void Include_Fields () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Include_Fields"); if (Fields_Available_Tree.isSelectionEmpty ()) { Dialog_Box.Notice ("Select one or more tables and/or fields."); return; } TreePath selections[] = Fields_Available_Tree.getSelectionPaths (), included; Vector expand_paths = new Vector (); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" " + selections.length + " selections"); DefaultMutableTreeNode root = (DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot (); for (int index = 0; index < selections.length; index++) { Object segments[] = selections[index].getPath (); DefaultMutableTreeNode node = (DefaultMutableTreeNode)segments[1]; String name = Name (node); DefaultMutableTreeNode catalog = Child (root, name), table, field; included = new TreePath (root); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" catalog: " + name); if (catalog == null && segments.length > 2) // Ignore catalog selection { Fields_Chosen_Tree_Model.insertNodeInto (catalog = new DefaultMutableTreeNode (name), root, root.getChildCount ()); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" > ADDED"); } if (segments.length > 2) { included = included.pathByAddingChild (catalog); node = (DefaultMutableTreeNode)segments[2]; if (node.getChildCount () > 0) // Ignore empty tables. { name = Name (node); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" table: " + name); if ((table = Child (catalog, name)) == null) { Fields_Chosen_Tree_Model.insertNodeInto (table = new DefaultMutableTreeNode (name), catalog, catalog.getChildCount ()); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" > ADDED"); } included = included.pathByAddingChild (table); if (segments.length > 3) { name = Name (segments[3]); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" field: " + name); if ((field = Child (table, name)) == null) { // Include the field. Fields_Chosen_Tree_Model.insertNodeInto (field = new DefaultMutableTreeNode (name), table, table.getChildCount ()); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" > ADDED"); } } else { // Include all fields of the table. Enumeration nodes = node.children (); while (nodes.hasMoreElements ()) { name = Name (nodes.nextElement ()); if (Child (table, name) == null) table.add (new DefaultMutableTreeNode (name)); } Fields_Chosen_Tree_Model.nodeStructureChanged (table); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" > ADDED all fields"); } expand_paths.add (included); } } } Iterator paths = expand_paths.iterator (); while (paths.hasNext ()) Fields_Chosen_Tree.expandPath ((TreePath)paths.next ()); if (root.getChildCount () != 0) Exclude_Button.setEnabled (true); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Include_Fields"); } // EXCLUDE fields from Chosen. private void Exclude_Fields () { DefaultMutableTreeNode root = (DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot (); if (root.getChildCount () == 0) return; // Nothing included. if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Exclude_Fields"); if (Fields_Chosen_Tree.isSelectionEmpty ()) { Dialog_Box.Notice ("Select one or more tables and/or fields."); return; } TreePath selections[] = Fields_Chosen_Tree.getSelectionPaths (); int selection = selections.length; // Bottom up processing. while (--selection >= 0) { DefaultMutableTreeNode parent, node = (DefaultMutableTreeNode)selections[selection] .getLastPathComponent (); do { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" Removing \"" + Name (node) + "\""); parent = (DefaultMutableTreeNode)node.getParent (); Fields_Chosen_Tree_Model.removeNodeFromParent (node); node = parent; } // Remove non-root empty nodes. while (node != root && node.getChildCount () == 0); } if (root.getChildCount () == 0) Exclude_Button.setEnabled (false); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Exclude_Fields"); } // JOIN fields. private void Join () { } // RESET all selections. private void Reset_Selections () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Reset_Selections"); Fields_Available_Tree.clearSelection (); Assemble_Fields (); ((DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot ()) .removeAllChildren (); Fields_Chosen_Tree_Model.reload (); Conditions.setText (null); /* Use this with JSpinner. Limit.setValue (new Integer (DEFAULT_LIMIT)); */ Limit.setText (String.valueOf (DEFAULT_LIMIT)); if (Update_Enabled > 0) { // Restore the edited values. Data_Table_Model data_table = Select_Table.Table_Model (); Vector edited_cells = data_table.Edited_Cells (); Iterator cells = edited_cells.iterator (); if ((DEBUG & DEBUG_SELECT_TABLE) != 0) System.out.println (" Database_View.Reset_Selections: restoring " + edited_cells.size () + " edited cells - "); while (cells.hasNext ()) { Cell cell = (Cell)cells.next (); data_table.setValueAt (cell.old_value, cell.row, cell.column); if ((DEBUG & DEBUG_SELECT_TABLE) != 0) System.out.println (" " + cell.row + ',' + cell.column + ": " + cell.new_value + '(' + cell.old_value + ')'); } // Reset all edits. data_table.Clear_Edited_Cells (); Reset_Update (); } if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Reset_Selections"); } // UPDATE edited data. private void Update () { } private void Reset_Update () { Update_Button.setBackground (Normal_Background_Color); if (Update_Enabled != UPDATE_DISABLED) Update_Enabled = UPDATE_ENABLED; } // SELECT the data. private void Select () { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (">>> Select"); String command = Assemble_Select_Command (); if (command == null) return; Run_Query (command); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< Select"); } // SQL command panel. private void SQL () { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (">>> SQL"); String command = Current_SQL_Command; if (command.length () == 0) command = Assemble_Select_Command (); command = SQL.Get_Command ("Select", command, this); if (command == null) return; Run_Query (command); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< SQL"); } private void Run_Query ( String SQL_command ) { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (">>> Run_Query:" + NL + SQL_command); final String SQL_query = SQL_command; int limit; /* Use this with JSpinner. try {limit = ((Integer)Limit.getValue ()).intValue ();} */ try {limit = new Integer (Limit.getText ()).intValue ();} catch (NumberFormatException e) { Dialog_Box.Error ("Use a numeric limit value."); Limit.setText (String.valueOf (DEFAULT_LIMIT)); return; } if (limit == 0) limit = -1; // Unlimited. final int max_records = limit; if ((DEBUG & DEBUG_SQL) != 0) System.out.println (" Limit: " + limit); Selections_Enabled (false); final Blinker blinking = new Blinker (Select_Button); blinking.start (); final Cursor cursor = getCursor (); setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR)); Thread query = new Thread () { private boolean Success = false; public void run () { try { String command = SQL.Clean (SQL_query); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("--- Run_Query:" + NL + command); Vector table = The_Database.Query (command, max_records); Success = true; Select_Table.Data (table, Update_Enabled != UPDATE_DISABLED); Reset_Update (); } catch (Database_Exception exception) {Dialog_Box.Error (exception.getMessage ());} SwingUtilities.invokeLater (new Runnable () { public void run () { Selections_Enabled (true); blinking.stop (); setCursor (cursor); if (Success) Set_Current_SQL_Command (SQL_query); }}); }}; query.start (); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< Run_Query"); } private String Assemble_Select_Command () { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (">>> Assemble_Select_Command"); Vector tables = Chosen_Tables (), fields = Chosen_Fields (); String conditions; Words conditions_words = new Words (SQL.Clean (Conditions.getText ())); if (conditions_words.Next_Word ().toUpperCase ().equals ("WHERE")) conditions = conditions_words.Substring (conditions_words.End_Index); else conditions = conditions_words.toString (); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ( " Fields: " + fields + NL + " Tables: " + tables + NL + " Conditions: " + conditions ); // Get the SQL command line from the Database. String command = null; try {command = ((String)The_Database.Select (tables, fields, conditions, 0) .firstElement ());} catch (Database_Exception e) {} if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< Assemble_Select_Command:" + NL + command); return command; } private void Set_Current_SQL_Command ( String command ) { if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("--- Set_Current_SQL_Command:" + NL + command); if (command == null) Current_SQL_Command = ""; else Current_SQL_Command = command; } // Enable or disable all selection buttons. private void Selections_Enabled ( boolean enable ) { Include_Button.setEnabled (enable); if (((DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot ()) .getChildCount () != 0) Exclude_Button.setEnabled (enable); Reset_Button.setEnabled (enable); SQL_Button.setEnabled (enable); Select_Button.setEnabled (enable); } class Select_Table_Changed implements TableModelListener { public void tableChanged ( TableModelEvent event ) { if ((DEBUG & DEBUG_SELECT_TABLE) != 0) { System.out.println ("--- Database_View.Select_Table_Changed:"); switch (event.getType ()) { case TableModelEvent.INSERT: System.out.print ("INSERT"); break; case TableModelEvent.UPDATE: System.out.print ("UPDATE"); break; case TableModelEvent.DELETE: System.out.print ("DELETE"); break; default: System.out.print ("Type " + event.getType ()); } System.out.println (": rows " + event.getFirstRow () + " to " + event.getLastRow () +", column " + event.getColumn ()); } if (Update_Enabled == UPDATE_ENABLED && event.getType () == TableModelEvent.UPDATE && event.getColumn () >= 0 && event.getFirstRow () >= 0 && event.getFirstRow () == event.getLastRow ()) { Update_Button.setBackground (Select_Table.Edited_Cell_Color ()); Update_Enabled = UPDATE_PENDING; } } } // End of class Select_Table_Changed. /*============================================================================== INSERT */ private JPanel Insertion_Panels () { JPanel panels = new JPanel (new GridBagLayout ()); /*.............................................................................. Fields panel */ JPanel fields_panel = new JPanel (new GridBagLayout ()); fields_panel.setBorder (new TitledBorder (new EtchedBorder (), " Fields ", TitledBorder.LEFT, TitledBorder.TOP)); GridBagConstraints location = new GridBagConstraints (); //- Row 0 // Col 0-1: Source fields tree. Insert_Fields_Tree = new JTree (Insert_Fields_Tree_Model); ((DefaultTreeCellRenderer)Insert_Fields_Tree.getCellRenderer ()) .setLeafIcon (null); Insert_Fields_Tree.setRootVisible (false); Insert_Fields_Tree.setShowsRootHandles (true); Insert_Fields_Tree.getSelectionModel () .setSelectionMode (TreeSelectionModel.SINGLE_TREE_SELECTION); ToolTipManager.sharedInstance().registerComponent(Insert_Fields_Tree); ((JComponent)Insert_Fields_Tree.getCellRenderer ()) .setToolTipText ("Database tables available for inserts"); JScrollPane source_fields = new JScrollPane (Insert_Fields_Tree); source_fields.setMinimumSize (new Dimension (100, 100)); source_fields.setPreferredSize (new Dimension (LIST_WIDTH, LIST_HEIGHT)); location.insets = new Insets (0, 5, 0, 0); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.gridwidth = 2; location.weightx = 1.0; location.weighty = 1.0; fields_panel.add (source_fields, location); // Col 1: strut (between lists) location.insets = new Insets (0, 0, 0, 0); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.gridwidth = 1; location.weightx = 0.0; location.weighty = 0.0; fields_panel.add (Box.createHorizontalStrut (10), location); // Col 1: box (corresponds to second list) location.insets = new Insets (0, 0, 0, 5); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; location.weighty = 1.0; fields_panel.add (new Box.Filler (new Dimension (100, 100), new Dimension (LIST_WIDTH, LIST_HEIGHT), new Dimension (100, 100)), location); //- Row 1 // Col 0: To Table button Insert_into_Button = new JButton ("To Table", INSERTION_ICON); Insert_into_Button.setMargin (new Insets (0, 0, 0, 3)); Insert_into_Button.setToolTipText ("Select the table to insert data"); Insert_into_Button.setMnemonic ('T'); Insert_into_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Insert_into_Table ();}}); location.insets = new Insets (0, 5, 5, 0); location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.gridwidth = 1; location.weightx = 0.0; location.weighty = 0.0; fields_panel.add (Insert_into_Button, location); // Col 1: Chosen table label Insertion_Table = new JLabel (); location.insets = new Insets (0, 5, 5, 5); location.fill = GridBagConstraints.HORIZONTAL; location.gridwidth = GridBagConstraints.REMAINDER; location.weightx = 1.0; fields_panel.add (Insertion_Table, location); location.insets = new Insets (5, 5, 3, 5); location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panels.add (fields_panel, location); /*.............................................................................. Buttons panel */ JPanel buttons_panel = new JPanel (new GridBagLayout ()); // Col 0: Clear button Clear_Button = new JButton ("Clear"); Clear_Button.setEnabled (false); Clear_Button.setToolTipText ("Clear pending inserts"); Clear_Button.setMnemonic ('C'); Clear_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Clear_Inserts ();}}); location.insets = new Insets (0, 0, 0, 0); location.gridwidth = 1; location.anchor = GridBagConstraints.WEST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; buttons_panel.add (Clear_Button, location); // Col 1: glue location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; buttons_panel.add (Box.createHorizontalGlue (), location); // Col 2: New button New_Button = new JButton ("New"); New_Button.setEnabled (false); New_Button.setToolTipText ("New record"); New_Button.setMnemonic ('N'); New_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {New_Record ();}}); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; buttons_panel.add (New_Button, location); // Col 3: strut fields_panel.add (Box.createHorizontalStrut (5), location); // Col 4: Insert button Insert_Button = new JButton ("Insert"); Insert_Button.setEnabled (false); Insert_Button.setToolTipText ("Insert the records"); Insert_Button.setMnemonic ('I'); Insert_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Insert ();}}); location.gridwidth = GridBagConstraints.REMAINDER; buttons_panel.add (Insert_Button, location); location.insets = new Insets (0, 7, 5, 7); location.anchor = GridBagConstraints.CENTER; location.fill = GridBagConstraints.BOTH; location.weightx = 0.0; location.weighty = 0.0; panels.add (buttons_panel, location); return panels; } /*============================================================================== Insertion Actions */ private void Insert_into_Table () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Insert_into_Table"); if (Insert_Fields_Tree.isSelectionEmpty ()) { Dialog_Box.Notice ("Select a table."); return; } // Assemble the table reference. Object path_segments[] = Insert_Fields_Tree.getSelectionPath ().getPath (); String table = null; if (path_segments.length > 2) { try {table = The_Database.Table_Reference (Name (path_segments[1]), Name (path_segments[2]));} catch (Database_Exception exception) {/* Won't happen */} if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (" table: " + table); } else { Dialog_Box.Notice ("Select a table."); return; } // Get the table's field names. Vector field_names; try {field_names = The_Database.Field_Names (table);} catch (Database_Exception exception) { Dialog_Box.Notice ("Table " + table + " is not accessable."); return; } // Provide an initial empty record in the insert table. Vector data = new Vector (1); data.add (Empty_Record (field_names.size ())); Insert_Table.Data (data, field_names, true); // Post the table reference. Insertion_Table.setText (table); // Enable the buttons. Clear_Button.setEnabled (true); New_Button.setEnabled (true); Insert_Button.setEnabled (true); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Insert_into_Table"); } private void Insert () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Insert"); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Insert"); } private void New_Record () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> New_Record"); Data_Table_Model data_table = Insert_Table.Table_Model (); data_table.addRow (Empty_Record (data_table.getColumnCount ())); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< New_Record"); } private void Clear_Inserts () { if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println (">>> Reset_Selections"); Insert_Table.Table_Model ().setRowCount (0); if ((DEBUG & DEBUG_UI_SELECTION) != 0) System.out.println ("<<< Reset_Selections"); } private Vector Empty_Record ( int size ) { Vector record = new Vector (size); while (size-- != 0) record.add (""); return record; } /*============================================================================== Helpers */ // Gets the list of all Chosen tables. private Vector Chosen_Tables () { Vector table_names = new Vector (); DefaultMutableTreeNode root = (DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot (); if (root.getChildCount () == 0) return table_names; Enumeration catalogs = root.children (); while (catalogs.hasMoreElements ()) { DefaultMutableTreeNode catalog = (DefaultMutableTreeNode)catalogs.nextElement (); Enumeration tables = catalog.children (); try { while (tables.hasMoreElements ()) table_names.add (The_Database.Table_Reference (Name (catalog), (Name ((DefaultMutableTreeNode)tables.nextElement ())))); } catch (Database_Exception exception) {/* Won't happen */} } return table_names; } // Gets the list of all Chosen fields. private Vector Chosen_Fields () { Vector field_names = new Vector (); DefaultMutableTreeNode root = (DefaultMutableTreeNode)Fields_Chosen_Tree_Model.getRoot (); if (root.getChildCount () == 0) return field_names; Enumeration catalogs; // Check for catalogs or tables which requires field name qualification. boolean qualify = false; if (root.getChildCount () > 1) qualify = true; else { catalogs = root.children (); while (catalogs.hasMoreElements ()) { if (((DefaultMutableTreeNode)catalogs.nextElement ()) .getChildCount () > 1) { qualify = true; break; } } } catalogs = root.children (); while (catalogs.hasMoreElements ()) { DefaultMutableTreeNode catalog = (DefaultMutableTreeNode)catalogs.nextElement (); Enumeration tables = catalog.children (); while (tables.hasMoreElements ()) { DefaultMutableTreeNode table = (DefaultMutableTreeNode)tables.nextElement (); String table_name = ""; if (qualify) { try {table_name = The_Database.Table_Reference (Name (catalog), Name (table)) + ".";} catch (Database_Exception exception) {/* Won't happen */} } Enumeration fields = table.children (); while (fields.hasMoreElements ()) field_names.add (table_name + Name ((DefaultMutableTreeNode)fields.nextElement ())); } } return field_names; } // Finds a named child of a parent. private static DefaultMutableTreeNode Child ( DefaultMutableTreeNode parent, String child_name ) { if (child_name != null) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.print ("--> Child: " + child_name + " of " + parent.getUserObject ()); DefaultMutableTreeNode child; Enumeration nodes = parent.children (); while (nodes.hasMoreElements ()) { child = (DefaultMutableTreeNode)nodes.nextElement (); if (Name (child).equals (child_name)) { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" found."); return child; } } } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" not found."); return null; } // Gets the name of a tree node. private static String Name ( Object node ) {return (String)((DefaultMutableTreeNode)node).getUserObject ();} private static void Load_Icons ( Object object ) { if (SELECT_ICON != null) return; char file_separator = System.getProperty ("file.separator").charAt (0); String package_name = object.getClass ().getName (); package_name = package_name.substring (0, package_name.lastIndexOf ('.')) .replace ('.', file_separator) + file_separator + "Icons" + file_separator; URL iconURL; iconURL = ClassLoader.getSystemResource (package_name + SELECT_ICON_NAME); if (iconURL != null) SELECT_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + INSERT_ICON_NAME); if (iconURL != null) INSERT_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + INCLUDE_ICON_NAME); if (iconURL != null) INCLUDE_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + EXCLUDE_ICON_NAME); if (iconURL != null) EXCLUDE_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + INSERTION_ICON_NAME); if (iconURL != null) INSERTION_ICON = new ImageIcon (iconURL); } } // End of Database_View class. pirl-2.3.8/PIRL/Database/Database.conf0000644000175000017500000000135510216462213017172 0ustar mathieumathieu# Data_View Configuration # # Database.conf,v 1.6 2005/03/18 05:18:03 castalia Exp Server = PIRL Group = PIRL Type = MySQL Host = pirldb.lpl.arizona.edu /* If you have a specific PIRL MySQL account enter the username and password here. NOTE: Do NOT enter PIRL computing systems account info! */ User = public Password = public Catalog = MOC Group = MOC Table = MGSC Group = MGSC Fields = VOLUME_ID, RATIONALE_DESC, ORBIT_NUMBER, SCALED_PIXEL_WIDTH, CENTER_LONGITUDE, CENTER_LATITUDE, SCALED_IMAGE_WIDTH, SCALED_IMAGE_HEIGHT, LINE_SAMPLES, IMAGE_LINES, INCIDENCE_ANGLE, PHASE_ANGLE, SUB_SOLAR_AZIMUTH End_Group End_Group End_Group Confirm_Connect = true Tooltips = true pirl-2.3.8/PIRL/Database/Query_DB.java0000644000175000017500000004242511742733571017154 0ustar mathieumathieu/* Query_DB PIRL CVS ID: Query_DB.java,v 3.9 2012/04/16 06:08:57 castalia Exp Copyright (C) 2008 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import java.util.Vector; import java.util.Iterator; /** Query_DB sends a SQL query to a Database and lists the results.

The Database server that is queried may be any for which the Database can obtain a Data_Port. Access information for the Database is provided in a Configuration file, which may be supplemented by command line options.

Database queries use standard SQL syntax. Depending on the specific database server that is accessed the syntax may have limitations and/or extensions.

The command line {@link #Usage() usage} description provides details on the application operation.

@author Bradford Castalia, Sean Whitsitt - UA/PIRL @version 3.9 @see PIRL.Database @see PIRL.Configuration */ public class Query_DB { /** The Class identification with revision number and date. */ public static final String ID = "PIRL.Database.Query_DB (3.9 2012/04/16 06:08:57)"; /** The default Configuration filename. */ public static final String DEFAULT_CONFIGURATION_FILENAME = Database.DEFAULT_CONFIGURATION_FILENAME; private static boolean Verbose = false; /** Prefix for the listing line that contains the SQL expression sent to the database server. */ public static final String SQL_QUERY_PREFIX = ">>> "; /** Prefix for the listing line that indicates a failed SQL query. */ public static final String SQL_QUERY_FAILED = "!!! Query failed."; /** Prefix for the data table field names line. */ public static final String FIELD_NAMES_PREFIX = "=== "; /** Delimiter between a data record number and its field values. */ public static final String DATA_RECORD_DELIMITER = " - "; /** Success exit status. */ public static final int EXIT_SUCCESS = 0; /** Exit status when invalid command line syntax was encounterd. */ public static final int EXIT_INVALID_COMMAND_LINE_SYNTAX = 1; /** Application exit status when there is a Configuration file problem. */ public static final int EXIT_CONFIGURATION_PROBLEM = 2; /** Application exit status when there is a problem accessing the Database. */ public static final int EXIT_DATABASE_ERROR = 3; /** Application exit status when one or more Database queries failed. */ public static final int EXIT_QUERY_FAILURE = 4; /** System new line sequence. */ private static final String NL = Database.NL; /*============================================================================== Application main */ /** Run the Query_DB application.

Exit status values:

{@link #EXIT_SUCCESS}
All queries succeeded.
{@link #EXIT_INVALID_COMMAND_LINE_SYNTAX}
The command line contains invalid syntax.
{@link #EXIT_CONFIGURATION_PROBLEM}
There was a problem with the configuration source. Either it couldn't be found or its contents are not valid PVL.
{@link #EXIT_DATABASE_ERROR}
A connection with the database server could not be established. This could be due to incorrect access information in the configuration.
{@link #EXIT_QUERY_FAILURE}
One or more queries failed. If multiple queries were specified some may have succeeded.

@param arguments The {@link #Usage() command line} arguments. */ public static void main ( String[] arguments ) { String configuration_source = null, server_name = null, catalog_name = null; Vector queries = new Vector (); boolean verbose = false; if (arguments.length == 0) Usage (); for (int count = 0; count < arguments.length; count++) { if (arguments[count].length () > 1 && arguments[count].charAt (0) == '-') { switch (arguments[count].toUpperCase ().charAt (1)) { case 'C': if (arguments[count].length () > 2 && arguments[count].toUpperCase ().charAt (2) == 'A') { // -CAtalog name if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.err.println ("Missing catalog name for " + arguments[--count] + " argument."); Usage (); } if (catalog_name != null) { System.err.println ("Multiple catalog names -" + NL + catalog_name + NL + "and" + NL + arguments[count]); Usage (); } catalog_name = arguments[count]; break; } // -Configuration name if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.err.println ("Missing configuration source name for " + arguments[--count] + " argument."); Usage (); } if (configuration_source != null) { System.err.println ("Multiple configuration files -" + NL + configuration_source + NL + "and" + NL + arguments[count]); Usage (); } configuration_source = arguments[count]; break; case 'S': // -Server name if (++count == arguments.length || arguments[count].length () == 0 || arguments[count].charAt (0) == '-') { System.err.println ("Missing server name for " + arguments[--count] + "argument."); Usage (); } if (server_name != null) { System.err.println ("Multiple server names -" + NL + server_name + NL + "and" + NL + arguments[count]); Usage (); } server_name = arguments[count]; break; case 'Q': // -Query SQL if (++count == arguments.length || arguments[count].length() == 0 || arguments[count].charAt(0) == '-') { System.err.println ("Missing query for " + arguments[--count] + " argument."); Usage (); } queries.add (arguments[count]); break; case 'V': // -Verbose verbose = true; break; default: System.err.println ("Unknown option: " + arguments[count]); case 'H': // Help. Usage (); } } else queries.add (arguments[count]); } if (queries.isEmpty ()) { System.err.println ("No query was specified."); Usage (); } // Configuration. if (configuration_source == null) configuration_source = DEFAULT_CONFIGURATION_FILENAME; Configuration configuration = null; try {configuration = new Configuration (configuration_source);} catch (Configuration_Exception exception) { System.err.println ("A configuration could not be created" + NL +"from " + configuration_source + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } configuration.Case_Sensitive (false); // Database. Database database = null; try { database = new Database (configuration); database.Connect (server_name, catalog_name); } catch (Database_Exception exception) { System.err.println ("A database connection could not be established." + NL + exception.getMessage ()); System.exit (EXIT_DATABASE_ERROR); } catch (Configuration_Exception exception) { System.err.println ("There was a problem with the configuration" + " while connecting to the database" + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } if (verbose) database.Add_SQL_Listener (new SQL_Listener () {public void SQL_Statement (String SQL) {System.out.println (SQL_QUERY_PREFIX + SQL);}}); // Queries. Vector> table; int status = EXIT_SUCCESS, index = -1, size = queries.size (); while (++index < size) { if (index != 0) System.out.println (); try { table = database.Query (queries.get (index)); Print_Table (table, true, true); } catch (Database_Exception exception) { status = EXIT_QUERY_FAILURE; if (verbose) System.out.println (SQL_QUERY_FAILED); System.err.println (exception.getMessage ()); } } try {database.Disconnect ();} catch (Database_Exception exception) { System.err.println ("There was a problem while disconnecting from the database." + NL + exception.getMessage ()); } System.exit (status); } /*============================================================================== Utilities */ /** Print a table of field records.

Each record in the table is listed as fields separated by a single horizontal tab character. Each record listed is terminated by a new-line sequence defined by the "line.separator" {@link System#getProperty(String) System property}.

N.B.: While data tables are typically rectangular, no presumption is made that each record will have the same number of field values.

If the prefix argument is true each line of the table listing will contain a prefix that identifies the table record. If the first_record_field_names argument is also true the prefix for the first record will be the {@link #FIELD_NAMES_PREFIX}. All other records will have as their prefix the record number followed by the {@link #DATA_RECORD_DELIMITER}.

@param table A Vector of Vector of Strings. If null nothing is done. @param prefix If true, each record listed will have an indentifying prefix; if false, no prefix will be listed. @param first_record_field_names If true, the first table record is taken to contain the column names of the field values that will be in the remaining records; if false, all table records are taken to contain field values. */ public static void Print_Table ( Vector> table, boolean prefix, boolean first_record_field_names ) { if (table == null) return; Vector record; int record_index = -1, record_index_adjust = first_record_field_names ? 0 : 1, total_records = table.size (), field_index, total_fields; while (++record_index < total_records) { if (prefix) { if (record_index == 0 && first_record_field_names) System.out.print (FIELD_NAMES_PREFIX); else System.out.print ((record_index + record_index_adjust) + DATA_RECORD_DELIMITER); } record = table.get (record_index); field_index = -1; total_fields = record.size (); while (++field_index < total_fields) { if (field_index != 0) System.out.print ("\t"); System.out.print (record.get (field_index)); } System.out.println (); } } /** Command line usage syntax.

Usage: Query_DB [<Options>] [-Query] <query> [...]
  Each query is an SQL query expression.
  Options -
    -Configuration [<filename>]
    -Server <server name>
    -CAtalog <catalog name>
    -Verbose
    -Help

Queries:

Each query is an SQL expression that will be run on the database server. Depending on the specific database server that is accessed the SQL query expression syntax may have limitations and/or extensions to the standard SQL syntax.

If the query contains spaces then it must be quoted; If it contains quotes then these must be escaped from shell interpretation (by a preceeding backslash) or the specification is already quoted then the specification quotes must be different from the enclosing quotes (use double quotes to enclose the specification and single quotes in the specification content). Other shell metacharacters - such as '*' or '!' - must also be escaped or enclosed in single quotes.

An example of a query expression to obtain all the records from the "Table" table in the "Catalog" catalog is:

'select * from Catalog.Table'

If the -catalog option had specified the "Catalog" catalog as the default then the "Catalog." portion of the expression would not be needed.

An example of a query expression that will list the "Field_1" and "Field_2" field values of "Table", using the default catalog, with a conditional clause that selects only those records having a "Text" field value of "text" and a "Number" field value of "123" is:

'select Field_1,Field_2 from Table where Text="text" and Number=123'

The result of each query is listed to the standard output. The first line of each query listing contains the selected field names; this line will be prefixed with {@link #FIELD_NAMES_PREFIX "=== "}. The following lines contain the values of the fields that were selected; each record line is prefixed with its record number (starting with 1) followed by the {@link #DATA_RECORD_DELIMITER " - "} delimiter. If no table records were obtained from the query only the field names will be listed. Each record line, including the line with the field names, separates each field with a single horizonatal tab character. If the -verbose option is specified the table listing is preceeded by a line beginning with {@link #SQL_QUERY_PREFIX ">>> "} listing the SQL query that was sent to the database server. If the query fails the SQL query line is followed by a {@link #SQL_QUERY_FAILED "!!! Query failed."} line. If the -verbose option is not specified and the query fails nothing will be listed for that query. A query failure will not prevent other queries from being sent to the database server. When multiple queries are specified each query result will be separated from the next query result by a single empty line.

All error reports are listed to the standard error output. If a query fails the failure report is listed to the standard error output.

Configuration:

If the name of the {@link PIRL.Configuration.Configuration Configuration} file is not specified the {@link #DEFAULT_CONFIGURATION_FILENAME} will be used. If the configuration file is not in the current working directory, it will be looked for in the user's home directory. The configuration file must contain the necessary information needed to identify and connect with the database server (as required by the {@link PIRL.Database.Database#Database(Configuration) Database} constructor and its {@link PIRL.Database.Database#Connect() Connect} method). These are typically the server "Host" name and database "Type", "User" and "Password" access values.

Only one configuration file may be used.

Database server name:

The Configuration file may contain connection information for more than one database. The information for each database is organized by {@link Database#SERVER Server} name, which may be specified. If no server name is provided, the Database will attempt to use the default (first) Server specified in the Configuration.

Only one database server may be used.

Database catalog name:

The default database catalog to use. This will override any {@link Database#CATALOG Catalog} parameter value from the configuration. The default catalog will only be used when a query expression does not specify a catalog.

Verbose

With the verbose option each query result is preceeded by listing the SQL expression sent to the database server. And, if the query fails, a "!!! Query failed." message will be listed instead of nothing.

Help:

The command line syntax usage is listed and the program exits. N.B.: If the -Help option occurs anywhere on the command line no other command line arguments will be used. If the command line is empty the usage will be listed.

N.B.The usage is printed to stderr. This method always results in a System.exit with a status of {@link #EXIT_INVALID_COMMAND_LINE_SYNTAX EXIT_INVALID_COMMAND_LINE_SYNTAX}. */ public static void Usage () { System.err.println ("Usage: Query_DB [] [-Query] [...]" + NL +" Each query is an SQL query expression." + NL +" Options -" + NL +" -Configuration " + " (default: " + DEFAULT_CONFIGURATION_FILENAME + ")" + NL +" -Server " + " (default: first configuration Server)" + NL +" -CAtalog " + " (default: Configuration Catalog parameter value)" + NL +" -Verbose" + " (default: false)" + NL +" -Help" + NL ); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Database/Makefile0000644000175000017500000000134111101456723016255 0ustar mathieumathieu# Makefile for Java classes # # PIRL CVS ID: Makefile,v 1.18 2008/10/28 00:33:23 castalia Exp # gmake syntax # Location of the Java Classes for Mathematics. JCM ?= /opt/java/jcm JPATH ?= ../..:$(JCM) JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = SQL_Listener.class \ Database.class \ Data_Port.class \ JDBC_Data_Port.class \ MySQL_Data_Port.class \ Database_Exception.class \ Data_Table.class \ SQL.class \ Database_View.class \ Data_View.class \ Update_DB.class \ Query_DB.class \ Fields_Map.class \ Connect_View.class \ PostgreSQL_Data_Port.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Database/Icons/0000755000175000017500000000000012052546523015675 5ustar mathieumathieupirl-2.3.8/PIRL/Database/Icons/Open_File16.gif0000644000175000017500000000022111750720603020363 0ustar mathieumathieuGIF89a̴̳̽{f33! ,@>Id k "cqIڨR)!r;_ & R";pirl-2.3.8/PIRL/Database/Icons/Save16.gif0000644000175000017500000000017311750720604017430 0ustar mathieumathieuGIF89afff!,@HoIA8 ,_H&\Z'U"K ,R#U)"}qL< $Q X¦rpރxC&`#q1Р-{s4`$d%g1`q@Cn$?[~ (UUSE }g\0Ո)ǫ(&Y6P=r PF,lB0% XDJ=(B8I # DS3 Ie> Nb$J@Vm`F 3@1JmEEBEPL0;pirl-2.3.8/PIRL/Database/Icons/Redo16.gif0000644000175000017500000000016711750720603017425 0ustar mathieumathieuGIF89a!,TR&" ݅4S:bI )˨Z ;pirl-2.3.8/PIRL/Database/Icons/LICENSE0000644000175000017500000000320712002334334016672 0ustar mathieumathieuCopyright 2000 by Sun Microsystems, Inc. All Rights Reserved. Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, and redistribute this software graphics artwork, as individual graphics or as a collection, as part of software code or programs that you develop, provided that i) this copyright notice and license accompany the software graphics artwork; and ii) you do not utilize the software graphics artwork in a manner which is disparaging to Sun. Unless enforcement is prohibited by applicable law, you may not modify the graphics, and must use them true to color and unmodified in every way. This software graphics artwork is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE GRAPHICS ARTWORK. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE GRAPHICS ARTWORK, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. If any of the above provisions are held to be in violation of applicable law, void, or unenforceable in any jurisdiction, then such provisions are waived to the extent necessary for this Disclaimer to be otherwise enforceable in such jurisdiction. pirl-2.3.8/PIRL/Database/Icons/LPL_Logo.gif0000644000175000017500000000077311750720603017777 0ustar mathieumathieuGIF89acd!Created with the GIMP! ,cdx0I 띯 ȍdh!h6k̾(cWxo H@K:4ڒN&^LoɌ|fq >ȻjA+3dhuZ6%0CDsE9D}~Xkz1Q2v,mpIʼ*̭x7]Őa"ӟ<8|n]YіfB ɳ0n_&#&!`Áadc^CIINp``O=h6sN ّ鰨љpHdz)NEA*t*UT˪uuwf~MT>876:aRV=W-߾661l'xl8/?rd˝A_YcQάYRk ;pirl-2.3.8/PIRL/Database/Icons/Arrow_Down16.gif0000644000175000017500000000152211750720603020611 0ustar mathieumathieuGIF89a @ ` @ @@@`@@@@@` `@``````` @` @` @` @`@ @@@`@@@@@ @ @@ @` @ @ @ @ @@@ @@@@@`@@@@@@@@@@`@ `@@`@``@`@`@`@`@@ @@@`@@@@@@ @@@`@@@@@@ @@@`@@@@@@ @@@`@@@@@ @` @ ` @ @@@`@@@@@` `@``````` @` @`ࠀ @` @` @` @ ` @ @@@`@@@@@` `@``````` @` @` @`𠠤!, / H(\B>hE?%Ҫxq`F!ZD8I;pirl-2.3.8/PIRL/Database/Icons/Export_24.gif0000644000175000017500000000041711750720603020151 0ustar mathieumathieuGIF89a00a==nžϑ򑑵ž !,'dihҾgl3=ŝӼ ?Td@`" f3ibEazuDDWn:@D~TѠ:uh@a]JY\D&}1 bp{"5k'V^d$!;pirl-2.3.8/PIRL/Database/SQL.java0000644000175000017500000011223511742733571016136 0ustar mathieumathieu/* SQL PIRL CVS ID: SQL.java,v 1.7 2012/04/16 06:08:57 castalia Exp Copyright (C) 2003-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Viewers.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.text.*; import javax.swing.event.*; import javax.swing.undo.*; import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Vector; import java.util.Hashtable; import java.util.Iterator; import java.net.URL; /** A modal dialog containing a text editor pane in which to specify an SQL command.

The {@link #Get_Command(String, String, Component) Get_Command} method will construct the dialog GUI, if it has not already been done, clears the text editor and then enables it for user interaction. When the user indicates that they are done the current contents of the text editor are returned; null will be returned if the Cancel button is used.

@author Bradford Castalia, UA/PIRL @version 1.7 */ public class SQL extends JDialog { private static final String ID = "PIRL.Database.SQL (1.7 2012/04/16 06:08:57)"; private static final Color KEYWORD_FOREGROUND_COLOR = new Color ((float)0.00, (float)0.42, (float)0.06), KEYWORD_BACKGROUND_COLOR = new Color ((float)0.91, (float)1.00, (float)0.90), OPERATION_FOREGROUND_COLOR = new Color ((float)0.00, (float)0.00, (float)1.00), OPERATION_BACKGROUND_COLOR = new Color ((float)0.92, (float)0.99, (float)1.00), MODIFIED_COLOR = new Color ((float)1.00, (float)1.00, (float)0.00); private static Color NORMAL_COLOR; private static final int DEFAULT_VIEW_WIDTH = 500, DEFAULT_VIEW_HEIGHT = 350; private static ImageIcon OPEN_ICON = null; private static final String OPEN_ICON_NAME = "Open_File16.gif"; private static ImageIcon SAVE_ICON; private static final String SAVE_ICON_NAME = "Save16.gif"; private static ImageIcon SAVE_AS_ICON; private static final String SAVE_AS_ICON_NAME = "SaveAs16.gif"; private static ImageIcon CUT_ICON; private static final String CUT_ICON_NAME = "Cut16.gif"; private static ImageIcon COPY_ICON; private static final String COPY_ICON_NAME = "Copy16.gif"; private static ImageIcon PASTE_ICON; private static final String PASTE_ICON_NAME = "Paste16.gif"; private static ImageIcon UNDO_ICON; private static final String UNDO_ICON_NAME = "Undo16.gif"; private static ImageIcon REDO_ICON; private static final String REDO_ICON_NAME = "Redo16.gif"; private static ImageIcon SQL_FILE_ICON; private static final String SQL_FILE_ICON_NAME = "Bar_Arrow_Right_24.gif"; // This SQL dialog. private static SQL _SQL_Dialog_ = null; // Reusable file chooser dialog. private static JFileChooser _File_Chooser_ = null; // The currently chosen file. private File Current_File = null; private JLabel Filename_Label; // SQL command text: private static String _Command_Line_; private JTextPane SQL_Pane; private boolean SQL_Modified = false; // Listeners for the SQL pane and its document. private Caret_Listener SQL_Caret_Listener = new Caret_Listener (); private Document_Listener SQL_Document_Listener = new Document_Listener (); private Undoable_Edit_Listener SQL_Edit_Listener = new Undoable_Edit_Listener (); // Editing: private static Hashtable Text_Actions = null; private static final String EDIT_CUT = "Cut", EDIT_COPY = "Copy", EDIT_PASTE = "Paste", EDIT_SELECT_ALL = "Select All", EDIT_UNDO = "Undo", EDIT_REDO = "Redo"; private JPopupMenu Popup_Menu; private UndoManager Undo_Manager = new UndoManager (); private Undo_Action Undo = new Undo_Action (); private Redo_Action Redo = new Redo_Action (); // Styles for the SQL text: private static final int BASIC_STYLE = 0, COMMENT_STYLE = 1, QUOTED_STYLE = 2, OPERATION_STYLE = 3, KEYWORD_STYLE = 4; private static final String STYLE_NAMES[] = { "BASIC", "COMMENT", "QUOTED", "OPERATION", "KEYWORD" }; private static final SimpleAttributeSet SQL_Styles[] = new SimpleAttributeSet[5]; static { SQL_Styles[BASIC_STYLE] = new SimpleAttributeSet (); SQL_Styles[COMMENT_STYLE] = new SimpleAttributeSet (); StyleConstants.setItalic (SQL_Styles[COMMENT_STYLE], true); SQL_Styles[QUOTED_STYLE] = new SimpleAttributeSet (); StyleConstants.setBold (SQL_Styles[QUOTED_STYLE], true); SQL_Styles[OPERATION_STYLE] = new SimpleAttributeSet (); StyleConstants.setForeground (SQL_Styles[OPERATION_STYLE], OPERATION_FOREGROUND_COLOR); StyleConstants.setBackground (SQL_Styles[OPERATION_STYLE], OPERATION_BACKGROUND_COLOR); StyleConstants.setBold (SQL_Styles[OPERATION_STYLE], true); SQL_Styles[KEYWORD_STYLE] = new SimpleAttributeSet (); StyleConstants.setForeground (SQL_Styles[KEYWORD_STYLE], KEYWORD_FOREGROUND_COLOR); StyleConstants.setBackground (SQL_Styles[KEYWORD_STYLE], KEYWORD_BACKGROUND_COLOR); StyleConstants.setBold (SQL_Styles[KEYWORD_STYLE], true); } private static final Vector SQL_OPERATIONS = new Vector (); static { SQL_OPERATIONS.add ("<"); SQL_OPERATIONS.add (">"); SQL_OPERATIONS.add ("<="); SQL_OPERATIONS.add (">="); SQL_OPERATIONS.add ("="); SQL_OPERATIONS.add ("!="); SQL_OPERATIONS.add ("AND"); SQL_OPERATIONS.add ("OR"); SQL_OPERATIONS.add ("NOT"); SQL_OPERATIONS.add ("LIKE"); } private static final Vector SQL_KEYWORDS = new Vector (); static { SQL_KEYWORDS.add ("SELECT"); SQL_KEYWORDS.add ("FROM"); SQL_KEYWORDS.add ("WHERE"); } // Document stylizer thread. //private Style_SQL Stylizer = new Style_SQL (); // System new-line sequence. private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_UI_SETUP = 1 << 0, DEBUG_FILE = 1 << 1, DEBUG_SQL = 1 << 2, DEBUG_GET = 1 << 3, DEBUG_OPS = 1 << 4, DEBUG_EDITS = 1 << 5, DEBUG_LISTENERS = 1 << 6, DEBUG_STYLE = 1 << 7, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ private SQL ( String title, Frame owner ) { super (owner, ((title == null) ? "SQL" : title), true); if ((DEBUG & DEBUG_UI_SETUP) != 0) System.out.println(">>> SQL: " + title); // Dialog main panel. Load_Icons (this); JPanel panel = new JPanel (new GridBagLayout ()); GridBagConstraints location = new GridBagConstraints (); // Filename label. Filename_Label = new JLabel ("", SwingConstants.LEFT); Filename_Label.setOpaque (true); NORMAL_COLOR = Filename_Label.getBackground (); location.insets = new Insets (0, 5, 0, 5); location.gridwidth = GridBagConstraints.REMAINDER; location.anchor = GridBagConstraints.WEST; panel.add (Filename_Label, location); // SQL command line panel. SQL_Pane = Create_SQL_Pane (); JScrollPane scroll_pane = new JScrollPane (SQL_Pane); scroll_pane.setPreferredSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_VIEW_HEIGHT)); scroll_pane.setMinimumSize (new Dimension (DEFAULT_VIEW_WIDTH, DEFAULT_VIEW_HEIGHT)); location.insets = new Insets (0, 5, 0, 5); location.gridwidth = GridBagConstraints.REMAINDER; location.fill = GridBagConstraints.BOTH; location.weightx = 1.0; location.weighty = 1.0; panel.add (scroll_pane, location); // Buttons. JButton button = new JButton ("Clear"); button.setMnemonic ('L'); button.setToolTipText ("Clear command"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Clear ();}}); location.insets = new Insets (5, 5, 5, 0); location.gridwidth = 1; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; location.weighty = 0.0; panel.add (button, location); location.fill = GridBagConstraints.HORIZONTAL; location.weightx = 1.0; panel.add (Box.createHorizontalGlue (), location); button = new JButton ("Cancel"); button.setMnemonic ('C'); button.setToolTipText ("Cancel operation"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Cancel ();}}); location.insets = new Insets (5, 0, 5, 0); location.anchor = GridBagConstraints.EAST; location.fill = GridBagConstraints.NONE; location.weightx = 0.0; panel.add (button, location); button = new JButton ("Execute"); button.setMnemonic ('X'); button.setToolTipText ("Execute command"); button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Accept ();}}); location.insets = new Insets (5, 0, 5, 5); location.gridwidth = GridBagConstraints.REMAINDER; panel.add (button, location); // Menu bars (set after Create_SQL_Pane for text edit actions). JMenuBar menu_bar = Create_Menus (); setJMenuBar (menu_bar); // Attach the Popup_Menu. MouseListener popup_listener = new Popup_Listener (); SQL_Pane.addMouseListener (popup_listener); menu_bar.addMouseListener (popup_listener); // Enable the Caret, Undoable_Edit and Document Listeners. Enable_Listeners (true); getContentPane ().add (panel, BorderLayout.CENTER); pack (); if ((DEBUG & DEBUG_UI_SETUP) != 0) System.out.println ("<<< SQL"); } /*============================================================================== Menus */ private JMenuBar Create_Menus () { // File menu. JMenu file_menu = new JMenu ("File"), file_menu_PU = new JMenu ("File"); file_menu.setMnemonic ('F'); JMenuItem menu_item = new JMenuItem ("Open...", OPEN_ICON), menu_item_PU = new JMenuItem ("Open..."); menu_item.setMnemonic ('O'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('O', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_File ();}}); menu_item_PU.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Open_File ();}}); file_menu.add (menu_item); file_menu_PU.add (menu_item_PU); menu_item = new JMenuItem ("Save", SAVE_ICON); menu_item_PU = new JMenuItem ("Save"); menu_item.setMnemonic ('S'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('S', Event.CTRL_MASK)); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_File ();}}); menu_item_PU.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_File ();}}); file_menu.add (menu_item); file_menu_PU.add (menu_item_PU); menu_item = new JMenuItem ("Save As...", SAVE_AS_ICON); menu_item_PU = new JMenuItem ("Save As..."); menu_item.setMnemonic ('A'); menu_item.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_File_As ();}}); menu_item_PU.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent event) {Save_File_As ();}}); file_menu.add (menu_item); file_menu_PU.add (menu_item_PU); // File chooser used with File menus. _File_Chooser_ = new JFileChooser (System.getProperty ("user.dir")); _File_Chooser_.setFileSelectionMode (JFileChooser.FILES_ONLY); // Edit menu. JMenu edit_menu = new JMenu ("Edit"), edit_menu_PU = new JMenu ("Edit"); edit_menu.setMnemonic ('E'); menu_item = edit_menu.add (Undo); edit_menu_PU.add (Undo); menu_item.setIcon (UNDO_ICON); menu_item.setMnemonic ('R'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('Z', Event.CTRL_MASK)); menu_item = edit_menu.add (Redo); edit_menu_PU.add (Redo); menu_item.setIcon (REDO_ICON); menu_item.setMnemonic ('U'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('Z', Event.SHIFT_MASK | Event.CTRL_MASK)); edit_menu.addSeparator(); edit_menu_PU.addSeparator(); Action action = (Action)Text_Actions.get (EDIT_CUT); menu_item = edit_menu.add (action); edit_menu_PU.add (action); menu_item.setIcon (CUT_ICON); menu_item.setMnemonic ('T'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('X', Event.CTRL_MASK)); action = (Action)Text_Actions.get (EDIT_COPY); menu_item = edit_menu.add (action); edit_menu_PU.add (action); menu_item.setIcon (COPY_ICON); menu_item.setMnemonic ('C'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('C', Event.CTRL_MASK)); action = (Action)Text_Actions.get (EDIT_PASTE); menu_item = edit_menu.add (action); edit_menu_PU.add (action); menu_item.setIcon (PASTE_ICON); menu_item.setMnemonic ('P'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('V', Event.CTRL_MASK)); edit_menu.addSeparator(); edit_menu_PU.addSeparator(); action = (Action)Text_Actions.get (EDIT_SELECT_ALL); menu_item = edit_menu.add (action); edit_menu_PU.add (action); menu_item.setMnemonic ('A'); menu_item.setAccelerator (KeyStroke.getKeyStroke ('A', Event.CTRL_MASK)); // Operations menu. JMenu ops_menu = new JMenu ("Operations"), ops_menu_PU = new JMenu ("Operations"); ops_menu.setMnemonic ('O'); Iterator operation = SQL_OPERATIONS.iterator (); while (operation.hasNext ()) { action = new AbstractAction ((String)operation.next ()) {public void actionPerformed (ActionEvent event) {Insert_Op (event);}}; ops_menu.add (action); ops_menu_PU.add (action); } // Assemble the menu bar. JMenuBar menu_bar = new JMenuBar (); menu_bar.add (file_menu); menu_bar.add (edit_menu); menu_bar.add (ops_menu); // Assemble the popup menu. Popup_Menu = new JPopupMenu (); Popup_Menu.add (ops_menu_PU); Popup_Menu.add (edit_menu_PU); Popup_Menu.add (file_menu_PU); return menu_bar; } /** Enables or disables the cut/copy edit menus. */ private void Edit_Menus_Enabled ( boolean enabled ) { ((Action)Text_Actions.get (EDIT_CUT)).setEnabled (enabled); ((Action)Text_Actions.get (EDIT_COPY)).setEnabled (enabled); } /*------------------------------------------------------------------------------ Operation Menu Action */ /** Insert an operation marker into the SQL command. */ private void Insert_Op ( ActionEvent event ) { if ((DEBUG & DEBUG_OPS) != 0) System.out.println ("Insert_Op: " + event.getActionCommand ()); AttributeSet old_attributes = SQL_Pane.getCharacterAttributes (); if ((DEBUG & DEBUG_OPS) != 0) { if (old_attributes.isEqual (SQL_Styles[COMMENT_STYLE])) System.out.println (" into " + STYLE_NAMES[COMMENT_STYLE]); else if (old_attributes.isEqual (SQL_Styles[QUOTED_STYLE])) System.out.println (" into " + STYLE_NAMES[QUOTED_STYLE]); } boolean listeners_off = ! ( old_attributes.isEqual (SQL_Styles[COMMENT_STYLE]) || old_attributes.isEqual (SQL_Styles[QUOTED_STYLE])); // Remove any selection. if (SQL_Pane.getSelectionStart () != SQL_Pane.getSelectionEnd ()) SQL_Pane.replaceSelection (null); // Insert the operation marker. SQL_Pane.replaceSelection (event.getActionCommand ()); } /*------------------------------------------------------------------------------ Popup Menu Listener */ class Popup_Listener extends MouseAdapter { public void mousePressed (MouseEvent event) {maybeShowPopup (event);} public void mouseReleased (MouseEvent event) {maybeShowPopup (event);} private void maybeShowPopup ( MouseEvent event ) { if (event.isPopupTrigger ()) Popup_Menu.show (event.getComponent (), event.getX (), event.getY ()); } } /*============================================================================== SQL Text Pane */ private JTextPane Create_SQL_Pane () { JTextPane pane = new JTextPane (); pane.setMargin (new Insets (10, 10, 10, 10)); if (Text_Actions == null) Text_Actions = Create_Action_Table (pane); // Rename the edit actions. Action action = (Action)Text_Actions.get (DefaultEditorKit.cutAction); action.putValue (Action.NAME, EDIT_CUT); action = (Action)Text_Actions.get (DefaultEditorKit.copyAction); action.putValue (Action.NAME, EDIT_COPY); action = (Action)Text_Actions.get (DefaultEditorKit.pasteAction); action.putValue (Action.NAME, EDIT_PASTE); action = (Action)Text_Actions.get (DefaultEditorKit.selectAllAction); action.putValue (Action.NAME, EDIT_SELECT_ALL); Text_Actions = Create_Action_Table (pane); return pane; } /** Sets the current SQL command String.

@param command The SQL command String. */ private void Set_Command ( String command ) { // Disable the Listeners during the change of text. Enable_Listeners (false); // Remove the current content. Document document = SQL_Pane.getDocument (); try { document.remove (0, document.getLength ()); if (command != null) { document.insertString (0, command, null); style_SQL (); Set_SQL_Modified (false); } } catch (BadLocationException exception) { Dialog_Box.Error ("Unexpected BadLocationException in Set_Command" + NL + "at offset " + exception.offsetRequested () + NL + NL + ID + NL + exception.getMessage (), _SQL_Dialog_); } Enable_Listeners (true); } /*------------------------------------------------------------------------------ PUBLIC INTERFACE */ /** Gets an SQL command String.

The SQL dialog window is initialized (if it has not been done already) and made visible. The resulting command line is returned.

@param title The title String for the dialog window. @param initial_SQL_command A String to use as the initial SQL command text. @param parent The parent window that dialog is associated with, which may be null. @return The SQL command String. This will be null if the Cancel button was used. */ public static String Get_Command ( String title, String initial_SQL_command, Component parent ) { if ((DEBUG & DEBUG_GET) != 0) System.out.println (">>> SQL.Get_Command: " + initial_SQL_command); if (_SQL_Dialog_ == null) _SQL_Dialog_ = new SQL (title, JOptionPane.getFrameForComponent (parent)); _Command_Line_ = null; _SQL_Dialog_.Reset (); _SQL_Dialog_.Set_Command (initial_SQL_command); _SQL_Dialog_.setLocationRelativeTo (parent); _SQL_Dialog_.setVisible (true); if ((DEBUG & DEBUG_GET) != 0) System.out.println ("<<< SQL.Get_Command: " + _Command_Line_ + NL + NL); return _Command_Line_; } public static String Get_Command ( String title, String initial_SQL_command ) {return Get_Command (title, initial_SQL_command, null);} public static String Get_Command ( String title, Component parent ) {return Get_Command (title, null, parent);} public static String Get_Command ( String title ) {return Get_Command (title, null, null);} public static String Get_Command () {return Get_Command (null, null, null);} /*------------------------------------------------------------------------------ Document Listeners WARNING: Don't modify the contents of the document within a Listener. */ protected class Caret_Listener implements CaretListener { public void caretUpdate (CaretEvent event) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println ("Caret_Listener: selection " + (event.getDot () != event.getMark ())); if (event.getDot () == event.getMark ()) Edit_Menus_Enabled (false); else Edit_Menus_Enabled (true); } } protected class Document_Listener implements DocumentListener { // Sees insertions into the document. public void insertUpdate (DocumentEvent event) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println ("Document_Listener: insertUpdate" + " Type " + event.getType () + " at " + event.getOffset () + ", length " + event.getLength ()); Set_SQL_Modified (true); style_SQL (); /* EventQueue.invokeLater (new Runnable () { public void run () { style_SQL (); }}); */ } // Sees removals from the document. public void removeUpdate (DocumentEvent event) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println ("Document_Listener: removeUpdate" + " Type " + event.getType () + " at " + event.getOffset () + ", length " + event.getLength ()); Set_SQL_Modified (true); style_SQL (); } // Sees changes to the document style. public void changedUpdate (DocumentEvent event) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println ("Document_Listener: changedUpdate" + " Type " + event.getType () + " at " + event.getOffset () + ", length " + event.getLength ()); } } /*============================================================================== SQL Style */ /** Apply styling to the SQL command.

Style attributes are applied to the SQL command based on the text content.

It could be argued that a custom EditorKit that overrides the read method should be used here. This should be done if things become too complicated, but this simple parser is fine for now. This method would become the basis for a read method or a parser class used by a read method. */ public void Style_SQL () { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (">>> Style_SQL"); StyledDocument document = SQL_Pane.getStyledDocument (); int end = document.getLength (); if (end == 0) { if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< Style_SQL: no text"); return; } // Prevent external document modification during the application of styles. synchronized (document) { StringBuffer text = null; try {text = new StringBuffer (document.getText (0, end).toUpperCase ());} catch (BadLocationException exception) {} // Can't happen. // Disable the Listeners during the document changes. Enable_Listeners (false); // Reset all styles. document.setCharacterAttributes (0, end, SQL_Styles[BASIC_STYLE], true); int style = BASIC_STYLE, index, start = -1; String marker = null; for (index = 0; index < end; index++) { if (start < 0) { // Check for the start of a special section. // Quoted sequences. if (text.charAt (index) == '"' || text.charAt (index) == '\'') { style = QUOTED_STYLE; start = index; marker = text.substring (index, index + 1); } // C-style comments. else if (text.charAt (index) == '/' && (index + 1) < end && text.charAt (index + 1) == '*') { style = COMMENT_STYLE; start = index; marker = "*/"; index++; } // #-style comments. else if (text.charAt (index) == '#') { style = COMMENT_STYLE; start = index; marker = NL; index += NL.length () - 1; } } else { // Find the section closing marker. if ((index = text.indexOf (marker, index)) < 0) // The section continues to the end of the text. index = end; else { if (style == QUOTED_STYLE && text.charAt (index - 1) == '\\') // Escaped quote. continue; index += marker.length (); } if ((DEBUG & DEBUG_SQL) != 0) System.out.println (start + "-" + (index - 1) + " " + STYLE_NAMES[style]); // Style the section. document.setCharacterAttributes (start, index - start, SQL_Styles[style], true); // Mask out the section. while (start < index) text.setCharAt (start++, (char)0); // Start looking for special sections again. start = -1; } } Iterator names = SQL_OPERATIONS.iterator (); style = OPERATION_STYLE; int length; boolean is_symbol; while (names.hasNext ()) { marker = (String)names.next (); length = marker.length (); is_symbol = ! Character.isLetter (marker.charAt (0)); start = 0; for (start = text.indexOf (marker, start); start >= 0; start = text.indexOf (marker, start)) { index = start + length; if (is_symbol || // A word must be free-standing. (start == 0 || text.charAt (start - 1) <= ' ') && (index == end || text.charAt (index) <= ' ')) { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (start + "-" + (index - 1) + " " + STYLE_NAMES[style] + ": " + marker); document.setCharacterAttributes (start, length, SQL_Styles[style], true); // Mask it. while (start < index) text.setCharAt (start++, (char)0); } else start = index; } } names = SQL_KEYWORDS.iterator (); style = KEYWORD_STYLE; while (names.hasNext ()) { marker = (String)names.next (); length = marker.length (); start = 0; for (start = text.indexOf (marker, start); start >= 0; start = text.indexOf (marker, start)) { index = start + length; // The word must be free-standing. if ((start == 0 || text.charAt (start - 1) <= ' ') && (index == end || text.charAt (index) <= ' ')) { if ((DEBUG & DEBUG_SQL) != 0) System.out.println (start + "-" + (index - 1) + " " + STYLE_NAMES[style] + ": " + marker); document.setCharacterAttributes (start, length, SQL_Styles[style], true); } start = index; } } Enable_Listeners (true); if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("<<< Style_SQL"); } // End of synchrozied section. } /** Apply styles to the SQL command document.

The call to Style_SQL method is wrapped in a Runnable for later invocation on the event queue because the operation may be initiated from within a document listener which has the document locked to prevent modification while in the listener. */ private void style_SQL () { if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("*** style_SQL: Style_SQL being queued."); EventQueue.invokeLater (new Runnable () { public void run () { Style_SQL (); }}); } public static String Clean ( String SQL_string ) { if ((DEBUG & DEBUG_STYLE) != 0) System.out.println (">>> SQL.Clean:" + NL + SQL_string + NL); StringBuffer clean_SQL = new StringBuffer (SQL_string); String marker = null; int start = 0, advance, index = 0, end = SQL_string.length (), NL_length = NL.length (); for (index = 0; index < end; index++) { if (marker == null) { // HT if (clean_SQL.charAt (index) == '\t') { clean_SQL.setCharAt (index, ' '); continue; } // NEW_LINE for (advance = 0; advance < NL_length && clean_SQL.charAt (index + advance) == NL.charAt (advance); advance++); if (advance == NL_length) { clean_SQL.replace (index, index + NL_length, " "); end -= NL_length + 1; index += NL_length - 1; continue; } // C-style comments. if (clean_SQL.charAt (index) == '/' && (index + 1) < end && clean_SQL.charAt (index + 1) == '*') { start = index; marker = "*/"; index++; } // #-style comments. else if (clean_SQL.charAt (index) == '#') { start = index; marker = NL; index += NL_length - 1; } } else { // Find the section closing marker. if ((index = clean_SQL.indexOf (marker, index)) < 0) // The section continues to the end of the text. index = end; else index += marker.length (); clean_SQL.delete (start, index); marker = null; end -= index - start; } } if ((DEBUG & DEBUG_STYLE) != 0) System.out.println ("<<< SQL.Clean:" + NL + clean_SQL + NL); return clean_SQL.toString (); } /*============================================================================== Undo/Redo */ class Undo_Action extends AbstractAction { public Undo_Action () { super (EDIT_UNDO); setEnabled (false); } public void actionPerformed (ActionEvent e) { try {Undo_Manager.undo ();} catch (CannotUndoException exception) {Dialog_Box.Warning ("Can't undo: " + exception, _SQL_Dialog_);} Update_State (); Redo.Update_State (); } protected void Update_State () { if (Undo_Manager.canUndo ()) { setEnabled (true); putValue (Action.NAME, Undo_Manager.getUndoPresentationName ()); Set_SQL_Modified (true); } else { setEnabled (false); putValue (Action.NAME, EDIT_UNDO); Set_SQL_Modified (false); } } } class Redo_Action extends AbstractAction { public Redo_Action () { super (EDIT_REDO); setEnabled (false); } public void actionPerformed ( ActionEvent event ) { try {Undo_Manager.redo ();} catch (CannotRedoException exception) {Dialog_Box.Warning ("Can't redo: " + exception, _SQL_Dialog_);} Update_State (); Undo.Update_State (); } protected void Update_State () { if (Undo_Manager.canRedo ()) { setEnabled (true); putValue (Action.NAME, Undo_Manager.getRedoPresentationName ()); } else { setEnabled (false); putValue (Action.NAME, EDIT_REDO); } } } /*.............................................................................. Edit Listener */ protected class Undoable_Edit_Listener implements UndoableEditListener { public void undoableEditHappened ( UndoableEditEvent event ) { if ((DEBUG & (DEBUG_EDITS | DEBUG_LISTENERS)) != 0) System.out.println ("--- Undoable_Edit_Listener::undoableEditHappened -" + NL + event.getEdit ().getPresentationName () + " is" + (event.getEdit ().isSignificant () ? " " : " not ") + "significant."); // Remember the edit and update the menus. Undo_Manager.addEdit (event.getEdit ()); Undo.Update_State (); Redo.Update_State (); } } /*============================================================================== File */ private void Open_File () { if (Save_Check () && _File_Chooser_.showOpenDialog (this) == JFileChooser.APPROVE_OPTION) { File file = _File_Chooser_.getSelectedFile (); if ((DEBUG & DEBUG_FILE) != 0) System.out.println ("Open_File: " + file.getPath ()); try { BufferedReader reader = new BufferedReader (new FileReader (file)); String new_text = "", line; while ((line = reader.readLine ()) != null) new_text += line + NL; reader.close (); Set_File (file); Set_Command (new_text); style_SQL (); Set_SQL_Modified (false); Undo_Manager.discardAllEdits (); } catch (FileNotFoundException exception) {Dialog_Box.Error ("Couldn't find the file: " + file.getPath () + NL + NL + ID + NL + exception.getMessage (), this);} catch (IOException exception) {Dialog_Box.Error ("Unable to read the file: " + file.getPath () + NL + NL + ID + NL + exception.getMessage (), this);} catch (Exception exception) {Dialog_Box.Error ("System error!" + NL + NL + ID + NL + exception.getMessage (), this);} } } private void Save_File () { if (SQL_Modified) { if (Current_File == null) Save_File_As (); else if (Write_File (Current_File)) Set_SQL_Modified (false); } } private boolean Save_File_As () { if (_File_Chooser_.showSaveDialog (this) == JFileChooser.APPROVE_OPTION) { File file = _File_Chooser_.getSelectedFile (); if ((DEBUG & DEBUG_FILE) != 0) System.out.println ("Save_File_As: " + file.getPath ()); if (Write_File (file)) { Set_File (file); Set_SQL_Modified (false); return true; } } return false; } /** Writes the current contents of the SQL command pane to the specified file.

@param file File to be written. @return true if the file was sucessfully written; false otherwise. */ private boolean Write_File ( File file ) { try { BufferedWriter writer = new BufferedWriter (new FileWriter (file)); String text = SQL_Pane.getText (); if ((DEBUG & DEBUG_FILE) != 0) System.out.println ("saving:" + NL + text); writer.write (text, 0, text.length ()); writer.close (); return true; } catch (IOException exception) { Dialog_Box.Error ("Unable to write the file: " + file.getPath () + NL + NL + ID + NL + exception.getMessage (), this); } catch (Exception exception) {Dialog_Box.Error ("System error!" + NL + NL + ID + NL + exception.getMessage (), this);} return false; } /** Checks if the SQL text has been changed and, if so, whether the user wants to save it. Then saves the text in a file if the user says Yes.

@return false if the user specified Cancel on the query to save modified SQL text, or the save failed; true otherwise. */ private boolean Save_Check () { if (SQL_Modified) { int check = Dialog_Box.Check (((Current_File == null) ? "Save the modified SQL" : ("Save the modified file:" + NL + Current_File.getPath ())), this); if (check == 0) // Cancel the new file. return false; if (check > 0) { if (Current_File == null && ! Save_File_As ()) return false; else if (! Write_File (Current_File)) return false; } } return true; } /** Sets the Current_File and displays the name. */ private void Set_File ( File file ) { String filename; if (file == null || file.getName ().equals ("")) { Current_File = null; filename = "(no file)"; Filename_Label.setText ("(no file)"); } else { Current_File = file; filename = Current_File.getAbsolutePath (); } if (SQL_Modified) filename += " (modified)"; Filename_Label.setText (filename); } /*============================================================================== Clear, Cancel, Accept Actions */ private void Clear () { if (Save_Check ()) Reset (); } private void Cancel () { if (Save_Check ()) setVisible (false); } private void Accept () { if (Save_Check ()) { _Command_Line_ = SQL_Pane.getText (); setVisible (false); } } /*============================================================================== Helpers */ /** Resets the Dialog to its initial empty state. */ private void Reset () { Set_File (null); // Clear the current File. Set_Command (null); // Remove the document content. Undo_Manager.discardAllEdits (); // Drop any undo edits. Edit_Menus_Enabled (false); // Disable the edit actions. } /** Sets the SQL_Modified state and updates the display to indicate it. */ private void Set_SQL_Modified ( boolean modified ) { if (SQL_Modified != modified) { // The modified state has changed. if ((DEBUG & DEBUG_SQL) != 0) System.out.println ("Set_SQL_Modified: " + modified); if (SQL_Modified = modified) { Filename_Label.setBackground (MODIFIED_COLOR); Edit_Menus_Enabled (true); } else { Filename_Label.setBackground (NORMAL_COLOR); Edit_Menus_Enabled (false); } Set_File (Current_File); // Update the filename display. } } private Hashtable Create_Action_Table ( JTextComponent text_component ) { Hashtable actions_table = new Hashtable (); Action[] actions = text_component.getActions (); for (int i = 0; i < actions.length; i++) { Action action = actions[i]; actions_table.put (action.getValue (Action.NAME), action); } return actions_table; } private void Enable_Listeners ( boolean enable ) { if ((DEBUG & DEBUG_LISTENERS) != 0) System.out.println ("--- Enable_Listeners: " + enable); StyledDocument document = SQL_Pane.getStyledDocument (); if (enable) { document.addUndoableEditListener (SQL_Edit_Listener); document.addDocumentListener (SQL_Document_Listener); SQL_Pane.addCaretListener (SQL_Caret_Listener); } else { document.removeUndoableEditListener (SQL_Edit_Listener); document.removeDocumentListener (SQL_Document_Listener); SQL_Pane.removeCaretListener (SQL_Caret_Listener); } } private static void Load_Icons ( Object object ) { if (OPEN_ICON != null) return; char file_separator = System.getProperty ("file.separator").charAt (0); String package_name = object.getClass ().getName (); package_name = package_name.substring (0, package_name.lastIndexOf ('.')) .replace ('.', file_separator) + file_separator + "Icons" + file_separator; URL iconURL; iconURL = ClassLoader.getSystemResource (package_name + OPEN_ICON_NAME); if (iconURL != null) OPEN_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + SAVE_ICON_NAME); if (iconURL != null) SAVE_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + SAVE_AS_ICON_NAME); if (iconURL != null) SAVE_AS_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + CUT_ICON_NAME); if (iconURL != null) CUT_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + COPY_ICON_NAME); if (iconURL != null) COPY_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + PASTE_ICON_NAME); if (iconURL != null) PASTE_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + UNDO_ICON_NAME); if (iconURL != null) UNDO_ICON = new ImageIcon (iconURL); iconURL = ClassLoader.getSystemResource (package_name + REDO_ICON_NAME); if (iconURL != null) REDO_ICON = new ImageIcon (iconURL); } /*============================================================================== Test stub */ private static String SQL_SELECT_test = "Select" + NL + "# SQL SELECT test" + NL + "a_field, b_field from the table" + NL + "/*" + NL + " Where conditions:" + NL + "*/" + NL + "Where a_field < 3" + NL + "and b_field = 'test'" + NL + "from"; public static void main (String[] args) { if (args.length > 0) SQL_SELECT_test = args[0]; final JFrame frame = new JFrame ("SQL"); frame.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent e) {System.exit (0);}}); JButton button = new JButton ("SQL..."); button.addActionListener (new ActionListener () { public void actionPerformed (ActionEvent e) { SQL_SELECT_test = SQL.Get_Command ("SQL test", SQL_SELECT_test, frame); System.out.println (SQL_SELECT_test); } }); JPanel contentPane = new JPanel (); frame.setContentPane (contentPane); contentPane.setLayout (new BoxLayout (contentPane, BoxLayout.Y_AXIS)); contentPane.setBorder (BorderFactory.createEmptyBorder (20, 20, 20, 20)); contentPane.add (button); button.setAlignmentX (JComponent.CENTER_ALIGNMENT); frame.pack (); frame.setVisible (true); } } pirl-2.3.8/PIRL/Database/MySQL_Data_Port.java0000644000175000017500000007101211742733571020376 0ustar mathieumathieu/* MySQL_Data_Port PIRL CVS ID: MySQL_Data_Port.java,v 1.23 2012/04/16 06:08:57 castalia Exp Copyright (C) 2001-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import java.util.Vector; /** Provides access to a MySQL database via a JDBC Data_Port.

@see JDBC_Data_Port @author Bradford Castalia, UA/PIRL @version 1.23 */ public class MySQL_Data_Port extends JDBC_Data_Port implements Data_Port { private static final String ID = "PIRL.Database.MySQL_Data_Port (1.23 2012/04/16 06:08:57)", Port_Type = "MySQL"; /** Required Configuration parameters and their default values.

Database.DRIVER
com.mysql.jdbc.Driver
Configuration.HOST
localhost
Database.CATALOG
mysql
*/ public static final String URL_Specs[][] = { {Database.DRIVER, "com.mysql.jdbc.Driver"}, {Configuration.HOST, "localhost"} }; /** The Configuration parameter to use when specifying a specific port for connection to the database server. */ public static final String PORT = "port"; /** Optional Configuration parameters.

These parameters are based on the use of the MySQL Connector-J Driver, version 3.1.13.

Connection/Authentication Properties

user
The name of the user to connect as. [none]
password
The password to use when connecting. [none]
socketFactory
The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor. [com.mysql.jdbc.StandardSocketFactory]
connectTimeout
Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. [0]
socketTimeout
Timeout on network socket operations (0 means no timeout). [0]
useConfigs
Load the comma-delimited list of configuration properties before parsing the URL or applying user-specified properties. These configurations are explained in the 'Configurations' of the documentation.
interactiveClient
Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout connections based on INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT. [false]
propertiesTransform
An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that the driver will use to modify URL properties passed to the driver before attempting a connection
useCompression
Use zlib compression when communicating with the server? [false]

High Availability and Clustering

autoReconnect
Should the driver attempt to re-connect if the connection dies (true or false)? [false]
autoReconnectForPools
Use a reconnection strategy appropriate for connection pools. [false]
failOverReadOnly
When failing over in autoReconnect mode, should the connection be set to 'read-only'? [true]
reconnectAtTxEnd
If autoReconnect is set to true, should the driver attempt reconnectionsat the end of every transaction? [false]
roundRobinLoadBalance
When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis? [false]
queriesBeforeRetryMaster
Number of queries to issue before falling back to master when failed over (when using multi-host failover). Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. [50]
secondsBeforeRetryMaster
How long should the driver wait (seconds), when failed over, before attempting to reconnect to the master server? Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. [30]
enableDeprecatedAutoreconnect
Auto-reconnect functionality is deprecated starting with version 3.2, and will be removed in version 3.3. Set this property to 'true' to disable the check for the feature being configured. [false]

Security

allowMultiQueries
Allow the use of ';' to delimit multiple queries during one statement. [false]
useSSL
Use SSL when communicating with the server? [false]
requireSSL
Require SSL connection if useSSL=true? [false]
allowUrlInLocalInfile
Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements? [false]
allowLoadLocalInfile
Should the driver allow use of 'LOAD DATA LOCAL INFILE...'. [true]
paranoid
Take measures to prevent exposure sensitive information in error messages and clear data structures holding sensitive data when possible? [false]

Performance

metadataCacheSize
The number of queries to cacheResultSetMetadata for if cacheResultSetMetaData is set to 'true' [50]
prepStmtCacheSize
If prepared statement caching is enabled, how many prepared statements should be cached? [25]
prepStmtCacheSqlLimit
If prepared statement caching is enabled, what's the largest SQL the driver will cache the parsing for? [256]
blobSendChunkSize
Chunk to use when sending BLOB/CLOBs via ServerPreparedStatements [1048576]
cacheCallableStmts
Should the driver cache the parsing stage of CallableStatements? [false]
cachePrepStmts
Should the driver cache the parsing stage of PreparedStatements? [false]
cacheResultSetMetadata
Should the driver cache ResultSetMetaData for Statements and PreparedStatements? (Req. JDK-1.4+) [false]
cacheServerConfiguration
Should the driver cache the results of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis? [false]
defaultFetchSize
The driver will call setFetchSize(n) with this value on all newly-created Statements [0]
dontTrackOpenResources
The JDBC specification requires the driver to automatically track and close resources, however if your application doesn't do a good job of explicitly calling close() on statements or result sets, this can cause memory leakage. Setting this property to true relaxes this constraint, and can be more memory efficient for some applications. [false]
dynamicCalendars
Should the driver retrieve the default calendar when required, or cache it per connection/session? [false]
elideSetAutoCommits
If using MySQL-4.1 or newer, should the driver only issue 'set autocommit=n' queries when the server's state doesn't match the requested state by Connection.setAutoCommit(boolean)? [false]
holdResultsOpenOverStatementClose
Should the driver close result sets on Statement.close() as required by the JDBC specification? [false]
rewriteBatchedStatements
Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(),the driver won't be able to determine the optimium number of parameters per batch and you might receive anan error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements. [false]
useFastIntParsing
Use internal String->Integer conversion routines to avoid excessive object creation? [true]
useLocalSessionState
Should the driver refer to the internal values of autocommit and transaction isolation that are set by Connection.setAutoCommit() and Connection.setTransactionIsolation(), rather than querying the database? [false]
useReadAheadInput
Use newer, optimized non-blocking, buffered input stream when reading from the server? [true]

Debugging and Profiling Properties

logger
The name of a class that implements 'com.mysql.jdbc.log.Log' that will be used to log messages to.(default logs to STDERR) [com.mysql.jdbc.log.StandardLogger]
profileSQL
Trace queries and their execution/fetch times to the configured logger? [false]
reportMetricsIntervalMillis
If 'gatherPerfMetrics' is enabled, how often should they be logged (in ms)? [30000]
maxQuerySizeToLog
Controls the maximum length/size of a query that will get logged when profiling or tracing [2048]
packetDebugBufferSize
The maximum number of packets to retain when 'enablePacketDebug' is true [20]
slowQueryThresholdMillis
If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'? [2000]
useUsageAdvisor
Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log? [false]
autoGenerateTestcaseScript
Should the driver dump the SQL it is executing, including server-side prepared statements to STDERR? [false]
dumpMetadataOnColumnNotFound
Should the driver dump the field-level metadata of a result set into the exception message when ResultSet.findColumn() fails? [false]
dumpQueriesOnException
Should the driver dump the contents of the query sent to the server in the message for SQLExceptions? [false]
enablePacketDebug
When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be kept, and dumped when exceptions are thrown in key areas in the driver's code [false]
explainSlowQueries
If 'logSlowQueries' is enabled, should the driver automatically issue an 'EXPLAIN' on the server and send the results to the configured log at a WARN level? [false]
logSlowQueries
Should queries that take longer than 'slowQueryThresholdMillis' be logged? [false]
traceProtocol
Should trace-level network protocol be logged? [false]

Miscelaneous Properties

useUnicode
Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8)? [false]
characterEncoding
If useUnicode is true, what character encoding should the driver use when dealing with strings? [autodetect]
characterSetResults
Character set to tell the server to return results as.
connectionCollation
If set, tells the server to use this collation via 'set collation_connection'
sessionVariables
A comma-separated list of name/value pairs to be sent as SET SESSION ... to the server when the driver connects.
allowNanAndInf
Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()? [false]
autoClosePStmtStreams
Should the driver automatically call .close() on streams/readers passed as arguments via set*() methods? [false]
autoDeserialize
Should the driver automatically detect and de-serialize objects stored in BLOB fields? [false]
capitalizeTypeNames
Capitalize type names in DatabaseMetaData? (usually only useful when using WebObjects) [false]
clobberStreamingResults
This will cause a 'streaming' ResultSet to be automatically closed, and any oustanding data still streaming from the server to be discarded if another query is executed before all the data has been read from the server. [false]
continueBatchOnError
Should the driver continue processing batch commands if one statement fails? The JDBC spec allows either way. [true]
CreateDatabaseIfNotExist
Creates the database given in the URL if it doesn't yet exist. Assumes the configured user has permissions to create databases. [false]
emptyStringsConvertToZero
Should the driver allow conversions from empty string fields to numeric values of '0'? [true]
emulateLocators
[false]
emulateUnsupportedPstmts
Should the driver detect prepared statements that are not supported by the server, and replace them with client-side emulated versions? [true]
ignoreNonTxTables
Ignore non-transactional table warning for rollback? [false]
jdbcCompliantTruncation
Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings(MySQL 4.1.0 and newer)? [true]
maxRows
The maximum number of rows to return (<=0 means return all rows). [-1]
noDatetimeStringSync
Don't ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString()) [false]
nullCatalogMeansCurrent
When DatabaseMetadataMethods ask for a 'catalog' parameter, does the value null mean use the current catalog? (this is not JDBC-compliant, but follows legacy behavior from earlier versions of the driver) [true]
nullNamePatternMatchesAll
Should DatabaseMetaData methods that accept *pattern parameters treat null the same as '%' (this is not JDBC-compliant, however older versions of the driver accepted this departure from the specification) [true]
overrideSupportsIntegrityEnhancementFacility
Should the driver return "true" for DatabaseMetaData.supportsIntegrityEnhancementFacility() even if the database doesn't support it to workaround applications that require this method to return "true" to signal support of foreign keys, even though the SQL specification states that this facility contains much more than just foreign key support (one such application being OpenOffice)? [false]
pedantic
Follow the JDBC spec to the letter. [false]
processEscapeCodesForPrepStmts
Should the driver process escape codes in queries that are prepared? [true]
relaxAutoCommit
If the version of MySQL the driver connects to does not support transactions, still allow calls to commit(), rollback() and setAutoCommit()? [false]
retainStatementAfterResultSetClose
Should the driver retain the Statement reference in a ResultSet after ResultSet.close() has been called. This is not JDBC-compliant after JDBC-4.0. [false]
rollbackOnPooledClose
Should the driver issue a rollback() when the logical connection in a pool is closed? [true]
runningCTS13
Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3 [false]
serverTimezone
Override detection/mapping of timezone. Used when timezone from server doesn't map to Java timezone. [none]
strictUpdates
Should the driver do strict checking (all primary keys selected) of updatable result sets? [true]
tinyInt1isBit
Should the driver treat the datatype TINYINT(1) as the BIT type (because the server silently converts BIT -> TINYINT(1) when creating tables)? [true]
transformedBitIsBoolean
If the driver converts TINYINT(1) to a different type, should it use BOOLEAN instead of BIT for future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT type? [false]
ultraDevHack
Create PreparedStatements for prepareCall() when required, because UltraDev is broken and issues a prepareCall() for _all_ statements? [false]
useGmtMillisForDatetimes
Convert between session timezone and GMT before creating Date and Timestamp instances (value of "false" is legacy behavior, "true" leads to more JDBC-compliant behavior. [false]
useHostsInPrivileges
Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges() (true/false), defaults to 'true'. [true]
useOldUTF8Behavior
Use the UTF-8 behavior the driver did when communicating with 4.0 and older servers [false]
useOnlyServerErrorMessages
Don't prepend 'standard' SQLState error messages to error messages returned by the server. [true]
useServerPrepStmts
Use server-side prepared statements if the server supports them? [true]
useSqlStateCodes
Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true' [true]
useStreamLengthsInPrepStmts
Honor stream length parameter in PreparedStatement/ResultSet.setXXXStream() method calls? [true]
useTimezone
Convert time/date types between client and server timezones? [false]
useUnbufferedInput
Don't use BufferedInputStream for reading data from the server? [true]
yearIsDateType
Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date, or as a SHORT? [true]
zeroDateTimeBehavior
What should happen when the driver encounters DATETIME values that are composed entirely of zeroes (used by MySQL to represent invalid dates)? Valid values are 'exception', 'round' and 'convertToNull'. [exception]
gatherPerfMetrics
Should the driver gather performance metrics, and report them via the configured logger every 'reportMetricsIntervalMillis' milliseconds? [false]
useNewIO
Should the driver use the java.nio.* interfaces for network communication? [false]
callableStmtCacheSize
If 'cacheCallableStmts' is enabled, how many callable statements should be cached? [100]
initialTimeout
If autoReconnect is enabled, the initial time to wait between re-connect attempts (seconds). [2]
maxReconnects
If autoReconnect is enabled, how many times should the driver attempt to reconnect? [3]
*/ public static final String Optional_Parameters[] = { "user", "password", "allowLoadLocalInfile", "allowUrlInLocalInfile", "allowMultiQueries", "allowNanAndInf", "autoClosePStmtStreams", "autoDeserialize", "autoReconnect", "autoReconnectForPools", "autoGenerateTestcaseScript", "blobSendChunkSize", "cacheCallableStmts", "cachePrepStmts", "cacheResultSetMetadata", "cacheServerConfiguration", "callableStmtCacheSize", "capitalizeTypeNames", "characterEncoding", "characterSetResults", "clobberStreamingResults", "connectTimeout", "connectionCollation", "continueBatchOnError", "createDatabaseIfNotExist", "defaultFetchSize", "dontTrackOpenResources", "dumpMetaDataOnColumnNotFound", "dumpQueriesOnException", "dynamicCalendars", "elideSetAutoCommits", "emptyStringsConvertToZero", "emulateLocators", "emulateUnsupportedPstmts", "enableDeprecatedAutoreconnect", "enablePacketDebug", "explainSlowQueries", "failOverReadOnly", "gatherPerfMetrics", "holdResultsOpenOverStatementClose", "ignoreNonTxTables", "initialTimeout", "interactiveClient", "jdbcCompliantTruncation", "logger", "logSlowQueries", "maxQuerySizeToLog", "maxReconnects", "maxRows", "metadataCacheSize", "noDatetimeStringSync", "nullCatalogMeansCurrent", "nullNamePatternMatchesAll", "overrideSupportsIntegrityEnhancementFacility", "paranoid", "packetDebugBufferSize", "pedantic", "prepStmtCacheSize", "prepStmtCacheSqlLimit", "processEscapeCodesForPrepStmts", "profileSQL", "propertiesTransform", "queriesBeforeRetryMaster", "reconnectAtTxEnd", "relaxAutoCommit", "reportMetricsIntervalMillis", "requireSSL", "retainStatementAfterResultSetClose", "rewriteBatchedStatements", "rollbackOnPooledClose", "roundRobinLoadBalance", "runningCTS13", "secondsBeforeRetryMaster", "serverTimezone", "sessionVariables", "slowQueryThresholdMillis", "socketFactory", "socketTimeout", "strictUpdates", "tinyInt1isBit", "traceProtocol", "transformedBitIsBoolean", "ultraDevHack", "useCompression", "useConfigs", "useFastIntParsing", "useGmtMillisForDatetimes", "useHostsInPrivileges", "useLocalSessionState", "useNewIO", "useOldUTF8Behavior", "useOnlyServerErrorMessages", "useReadAheadInput", "useServerPrepStmts", "useSqlStateCodes", "useSSL", "useStreamLengthsInPrepStmts", "useTimezone", "useUnbufferedInput", "useUnicode", "useUsageAdvisor", "yearIsDateType", "zeroDateTimeBehavior" }; private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_OPEN = 1 << 0, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Creates the abstract JDBC_Data_Port finalizer class and registers the class ID and name of the Data_Port type. */ public MySQL_Data_Port () { ID_Type (ID, Port_Type); Treat_Schema_As_Catalog = false; } /*============================================================================== Methods */ // Data_Port Implementation /** Implements the Data_Port Parameters method.

@see Data_Port#Parameters() */ public Configuration Parameters () throws Database_Exception { try { Parameter parameters = new Parameter (Port_Type), group = new Parameter ("Required"); int index; for (index = 0; index < URL_Specs.length; index++) group.Add (new Parameter (URL_Specs[index][0]) .Value (URL_Specs[index][1])); parameters.Add (group); group = new Parameter ("Optional"); group.Add (new Parameter (PORT)); group.Add (new Parameter (Database.CATALOG)); for (index = 0; index < Optional_Parameters.length; index++) group.Add (new Parameter (Optional_Parameters[index])); parameters.Add (group); return new Configuration (parameters); } catch (Exception exception) { // This shouldn't happen if the coding is correct. throw new Database_Exception ( ID + NL + exception.getMessage () + NL +"There is a coding flaw in the Parameters method!" ); } } // Data_Port Implementation /** Implements the Data_Port Open method.

The Configuration that is supplied is conditionally set (existing parameters of the same name are not reset) with the required {@link #URL_Specs URL specifications} default values. Then the Configuration is provided to the base JDBC_Data_Port {@link JDBC_Data_Port#Configure Configure} method to load the JDBC driver and save the Configuration.

The URL specifying how the connection will be made to the database server has the form:

jdbc:mysql://[HOST][:PORT]/CATALOG[?param1=value1[&param2=value2][...]]

All of the parameters will be sought in the Configuration. The paramN options are in the {@link #Optional_Parameters optional parameters} list. Parameters with these names in the Configuration will be included in the URL, otherwise they will be omitted.

The URL specification is provided to the JDBC_Data_Port {@link JDBC_Data_Port#Open_Data_Port Open_Data_Port} method which opens a connection to the database server.

@param configuration The Configuration for this Data_Port. @throws Database_Exception If no Configuration is provided or the JDBC_Data_Port fails to load the driver or make the connection to the database server. */ public void Open ( Configuration configuration ) throws Database_Exception { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (">>> MySQL_Data_Port.Open: Configuration -" + NL + configuration.Description ()); // Setup the configuration. if (configuration == null) throw new Database_Exception ( ID + NL +"An invalid (null) configuration was specified." ); // Don't be case sensitive here. boolean case_sensitive = configuration.Case_Sensitive (false); try { // Set the required parameters. if ((DEBUG & DEBUG_OPEN) != 0) { System.out.println (" MySQL_Data_Port.Open: Set_Conditionally (URL_Specs) -"); for (int element = 0; element < URL_Specs.length; element++) System.out.println (" " + URL_Specs[element][0] +" - " + URL_Specs[element][1]); } configuration.Set_Conditionally (URL_Specs); // Register the configuration, which loads our driver. if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" MySQL_Data_Port.Open: Configure"); Configure (configuration); // Assemble the URL. String URL = "jdbc:" + Port_Type + "://" + Config_Value (Configuration.HOST); String URL_part; if (! (URL_part = Config_Value (PORT)).equals ("")) URL += ":" + URL_part; URL += "/" + Config_Value (Database.CATALOG); if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" MySQL_Data_Port.Open: Assembling the URL -" + NL +" Port_Type - " + Port_Type + NL +" HOST - " + Config_Value (Configuration.HOST) + NL +" PORT - " + Config_Value (PORT) + NL +" Catalog - " + Config_Value (Database.CATALOG) + NL +" ->URL - " + URL + NL +" Optional parameters -"); // Optional parameters: String delimiter = "?"; for (int part = 0; part < Optional_Parameters.length; part++) { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" " + Optional_Parameters[part] +" - " + Config_Value (Optional_Parameters[part])); URL_part = Config_Value (Optional_Parameters[part]); if (! URL_part.equals ("")) { URL += delimiter + Optional_Parameters[part] + "=" + URL_part; if (delimiter.equals ("?")) delimiter = "&"; } } // Open the data port connection. if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" MySQL_Data_Port.Open: Open_Data_Port (" + URL + ")"); Open_Data_Port (URL); } catch (Configuration_Exception exception) { configuration.Case_Sensitive (case_sensitive); // Copy the exception with password masked message. Configuration_Exception configuration_exception = new Configuration_Exception (Database_Exception.masked_String (exception.getMessage ()), exception.getCause ()); configuration_exception.setStackTrace (exception.getStackTrace ()); throw new Database_Exception (configuration_exception); } catch (Database_Exception exception) { configuration.Case_Sensitive (case_sensitive); throw exception; } } /** Specialization of the interface's Query method to provide implementation of the limit functionality using the MySQL specific capability.

@see JDBC_Data_Port#Query(String, int) */ public Vector Query ( String SQL_query, int limit ) throws Database_Exception { if (limit > 0 && SQL_query != null && SQL_query.toUpperCase ().indexOf ("LIMIT") < 0) SQL_query += " LIMIT " + limit; return super.Query (SQL_query, limit); } } // End of class pirl-2.3.8/PIRL/Database/PostgreSQL_Data_Port.java0000644000175000017500000003653311742733571021445 0ustar mathieumathieu/* PostgreSQL_Data_Port PIRL CVS: $ID$ Copyright (C) 2008 Arizona Board of Regents on behalf of the Planetary Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.PVL.Parameter; import PIRL.PVL.Value; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import java.util.Vector; /** Provides access to a PostgreSQL database via a JDBC_Data_Port.

@see JDBC_Data_Port @author Bradford Castalia, UA/PIRL @version 1.5 */ public class PostgreSQL_Data_Port extends JDBC_Data_Port implements Data_Port { /** Class name and version identification. */ public static final String ID = "PIRL.Database.PostgreSQL_Data_Port (1.19 2006/08/09 00:50:32)"; /** Data_Port Type name. */ public static final String Port_Type = "PostgreSQL"; /** Required Configuration parameters and their default values.

{@link Database#DRIVER}
org.postgresql.Driver
{@link Configuration#HOST}
localhost
{@link Database#DATABASE}
public
*/ public static final String URL_Specs[][] = { {Database.DRIVER, "org.postgresql.Driver"}, {Configuration.HOST, "localhost"}, {Database.DATABASE, "public"} }; /** The Configuration parameter to use when specifying a specific port for connection to the database server. */ public static final String PORT = "port"; /** Optional Configuration parameters.

These parameters are based on the use of the PostgreSQL JDBC driver, version 8.3.

Connection Parameters

user
The database user on whose behalf the connection is being made. [none]
password
The database user's password. [none]
ssl
Connect using SSL. The driver must have been compiled with SSL support. This property does not need a value associated with it. The mere presence of it specifies a SSL connection. However, for compatibility with future versions, the value "true" is preferred. [false]
sslfactory
The provided value is a class name to use as the SSLSocketFactory when establishing a SSL connection. [none]
sslfactoryarg
This value is an optional argument to the constructor of the sslfactory class provided above. [none]
compatible
Act like an older version of the driver to retain compatibility with older applications. At the moment this controls two driver behaviors: the handling of binary data fields, and the handling of parameters set via setString(). Older versions of the driver used this property to also control the protocol used to connect to the backend. This is now controlled by the protocolVersion property. [none]
protocolVersion
The driver supports both the V2 and V3 frontend/backend protocols. The V3 protocol was introduced in 7.4 and the driver will by default try to connect using the V3 protocol, if that fails it will fall back to the V2 protocol. If the protocolVersion property is specified, the driver will try only the specified protocol (which should be either "2" or "3"). Setting protocolVersion to "2" may be used to avoid the failed attempt to use the V3 protocol when connecting to a version 7.3 or earlier server, or to force the driver to use the V2 protocol despite connecting to a 7.4 or greater server. [3]
loglevel
Set the amount of logging information printed to the DriverManager's current value for LogStream or LogWriter. It currently supports values of org.postgresql.Driver.DEBUG (2) and org.postgresql.Driver.INFO (1). INFO will log very little information while DEBUG will produce significant detail. This property is only really useful if you are a developer or are having problems with the driver. [0]
charSet
The character set to use for data sent to the database or recieved from the database. This property is only relevent for server versions less than or equal to 7.2. The 7.3 release was the first with multibyte support compiled by default and the driver uses its character set translation facilities instead of trying to do it itself. [none]
allowEncodingChanges
When using the V3 protocol the driver monitors changes in certain server configuration parameters that should not be touched by end users. The client_encoding setting is set by the driver and should not be altered. If the driver detects a change it will abort the connection. There is one legitimate exception to this behavior though, using the COPY command on a file residing on the server's filesystem. The only means of specifying the encoding of this file is by altering the client_encoding setting. The JDBC team considers this a failing of the COPY command and hopes to provide an alternate means of specifying the encoding in the future, but for now there is this URL parameter. Enable this only if you need to override the client encoding when doing a copy. [false]
logUnclosedConnections
Clients may leak Connection objects by failing to call its close() method. Eventually these objects will be garbage collected and the finalize() method will be called which will close the Connection if caller has neglected to do this himself. The usage of a finalizer is just a stopgap solution. To help developers detect and correct the source of these leaks the logUnclosedConnections URL parameter has been added. It captures a stacktrace at each Connection opening and if the finalize() method is reached without having been closed the stacktrace is printed to the log. [false]
prepareThreshold
Determine the number of PreparedStatement executions required before switching over to use server side prepared statements. The default is five, meaning start using server side prepared statements on the fifth execution of the same PreparedStatement object. [5]
loginTimeout
Specify how long to wait for establishment of a database connection. The timeout is specified in seconds. [Pulled timeout from DriverManager.getLoginTimeout()
stringtype
Specify the type to use when binding PreparedStatement parameters set via setString(). If stringtype is set to varchar (the default), such parameters will be sent to the server as varchar parameters. If stringtype is set to unspecified, parameters will be sent to the server as untyped values, and the server will attempt to infer an appropriate type. This is useful if you have an existing application that uses setString() to set parameters that are actually some other type, such as integers, and you are unable to change the application to use an appropriate method such as setInt(). [varchar]
*/ public static final String Optional_Parameters[] = { "user", "password", "ssl", "sslfactory", "sslfactoryarg", "compatible", "protocolVersion", "loglevel", "charSet", "allowEncodingChanges", "logUnclosedConnections", "prepareThreshold", "loginTimeout", "stringtype" }; private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_OPEN = 1 << 0, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs the abstract JDBC_Data_Port finalizer class, registers the class ID and name of the Data_Port type, and sets the flag to treat schema as Database "catalogs". */ public PostgreSQL_Data_Port () { ID_Type (ID, Port_Type); Treat_Schema_As_Catalog = true; } /*============================================================================== Methods */ // Data_Port Implementation /** Implements the Data_Port Parameters method.

@see Data_Port#Parameters() */ public Configuration Parameters () throws Database_Exception { try { Parameter parameters = new Parameter (Port_Type), group = new Parameter ("Required"); int index; for (index = 0; index < URL_Specs.length; index++) group.Add (new Parameter (URL_Specs[index][0]) .Value (URL_Specs[index][1])); parameters.Add (group); group = new Parameter ("Optional"); group.Add (new Parameter (PORT)); group.Add (new Parameter (Database.CATALOG)); for (index = 0; index < Optional_Parameters.length; index++) group.Add (new Parameter (Optional_Parameters[index])); parameters.Add (group); return new Configuration (parameters); } catch (Exception exception) { // This shouldn't happen if the coding is correct. throw new Database_Exception ( ID + NL +"There is a coding flaw in the Parameters method!" + NL + exception.getMessage () ); } } // Data_Port Implementation /** Implements the Data_Port Open method.

The Configuration that is supplied is conditionally set (existing parameters of the same name are not reset) with the required {@link #URL_Specs URL specifications} default values. Then the Configuration is provided to the base JDBC_Data_Port {@link JDBC_Data_Port#Configure Configure} method to load the JDBC driver and save the Configuration.

The URL specifying how the connection will be made to the database server has the form:

jdbc:postgresql://[HOST][:PORT]/CATALOG[?param1=value1[&param2=value2][...]]

All of the parameters will be sought in the Configuration. The paramN options are in the {@link #Optional_Parameters optional parameters} list. Parameters with these names in the Configuration will be included in the URL, otherwise they will be omitted.

The URL specification is provided to the JDBC_Data_Port {@link JDBC_Data_Port#Open_Data_Port Open_Data_Port} method which opens a connection to the database server.

@param configuration The Configuration for this Data_Port. @throws Database_Exception If no Configuration is provided or the JDBC_Data_Port fails to load the driver or make the connection to the database server. */ public void Open ( Configuration configuration ) throws Database_Exception { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (">>> PostgreSQL_Data_Port.Open: Configuration -" + NL + configuration.Description ()); // Setup the configuration. if (configuration == null) throw new Database_Exception ( ID + NL +"An invalid (null) configuration was specified." ); // Don't be case sensitive here. boolean case_sensitive = configuration.Case_Sensitive (false); try { // Set the required parameters. if ((DEBUG & DEBUG_OPEN) != 0) { System.out.println (" PostgreSQL_Data_Port.Open: Set_Conditionally (URL_Specs) -"); for (int element = 0; element < URL_Specs.length; element++) System.out.println (" " + URL_Specs[element][0] +" - " + URL_Specs[element][1]); } configuration.Set_Conditionally (URL_Specs); // Register the configuration, which loads our driver. if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" PostgreSQL_Data_Port.Open: Configure"); Configure (configuration); // Assemble the URL. String URL = "jdbc:" + Port_Type.toLowerCase () + "://" + Config_Value (Configuration.HOST); String URL_part; if (! (URL_part = Config_Value (PORT)).equals ("")) URL += ":" + URL_part; URL += "/" + Config_Value (Database.DATABASE); if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" PostgreSQL_Data_Port.Open: Assembling the URL -" + NL +" Port_Type - " + Port_Type.toLowerCase () + NL +" HOST - " + Config_Value (Configuration.HOST) + NL +" PORT - " + Config_Value (PORT) + NL +" DATABASE - " + Config_Value (Database.DATABASE) + NL +" ->URL - " + URL + NL +" Optional parameters -"); // Optional parameters: String delimiter = "?"; for (int part = 0; part < Optional_Parameters.length; part++) { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" " + Optional_Parameters[part] +" - " + Config_Value (Optional_Parameters[part])); URL_part = Config_Value (Optional_Parameters[part]); if (! URL_part.equals ("")) { URL += delimiter + Optional_Parameters[part] + "=" + URL_part; if (delimiter.equals ("?")) delimiter = "&"; } } // Open the data port connection. if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" PostgreSQL_Data_Port.Open: Open_Data_Port (" + URL + ")"); Open_Data_Port (URL); } catch (Configuration_Exception exception) { configuration.Case_Sensitive (case_sensitive); // Copy the exception with password masked message. Configuration_Exception configuration_exception = new Configuration_Exception (Database_Exception.masked_String (exception.getMessage ()), exception.getCause ()); configuration_exception.setStackTrace (exception.getStackTrace ()); throw new Database_Exception (configuration_exception); } catch (Database_Exception exception) { configuration.Case_Sensitive (case_sensitive); throw exception; } } // Data_Port Implementation /** Specialization of the Query method to provide implementation of the limit functionality using the server specific capability.

@see JDBC_Data_Port#Query(String, int) */ public Vector Query ( String SQL_query, int limit ) throws Database_Exception { if (limit > 0 && SQL_query != null && SQL_query.toUpperCase ().indexOf ("LIMIT") < 0) SQL_query += " LIMIT " + limit; return super.Query (SQL_query, limit); } // Data_Port Implementation /** Specialization of the Rename table functionality using server specific syntax.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. @param name The new name for the table. If this is null, nothing is done. @throws Database_Exception If no catalog or table name is available, or the database server rejected the operation. */ public void Rename ( String table, String name ) throws Database_Exception { if (name != null) { String catalog = catalog_name ("Rename table", table); table = table_name ("Rename table", table); try { if (Database.Matches (Tables (catalog), table, Case_Sensitive_Identifiers)) Update ("ALTER TABLE " + catalog + Component_Delimiter + table + " RENAME TO " + name); } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Rename table \"" + table + "\" to \"" + name + "\".", exception ); } } } } pirl-2.3.8/PIRL/Database/package.html0000644000175000017500000001053211742733571017112 0ustar mathieumathieu The PIRL Database package provides a simple interface for access to, and basic management of, databases.

A database is considered to be composed of named data tables grouped into named catalogs. Each table is composed of records containing the same number of named data fields. A table might be thought of as a rectangular array of data where each column has a name and data type, and each row provides a set of specific data values for all the columns.

A database may be implemented as a simple text file or a complex Relational Database System managed by a server on a remote system. The Database user need not be concerned with how the database is implemented. The Database class provides a consistent Data_Port interface for all databases, so the user can focus on the data rather than the mechanics of the database implementation. Each Database object is backed by a specific type of Data_Port implementation that takes care of the requirements of its type of database. For example, a Data_Port class for accessing MySQL database servers is provided. This class is supported by a class that can be used with any database accessible via a JDBC driver; the MySQL_Data_Port class just provides the knowledge of how to Open access to a MySQL database using its specific driver. Support for other types of databases is very easy to provide.

A Configuration class is provided to manage parameters that are needed for access to specific databases. The parameters may be maintained in a text file ("Database.conf" by default) using the Parameter-Value Language syntax. In this way applications using the Database package do not need to know anything at all about particular databases; everything can be specified in the configuration file and grouped by database type as well as any other groupings that may be appropriate. This includes the names of JDBC drivers and their specific access parameters, catalog and table names, data field names mapped to generic application names, and default values for parameters that may or may not have specific parameters in some grouping. Of course the application may directly manage configurations as it sees fit. When a connection to a particular type of database is made, the Database class collects all the parameters relevant to it together in a separate Configuration associated with the Data_Port object, so the Data_Port automatically gets the correct parameters, and the application may know exactly which parameters are being used.

The Data_Port interface provides a complement of methods for the typical operations on a database: catalog (a.k.a. "database") creation and deletion; table creation, deletion and modification including adding, removing and renaming/retyping fields; field insertion, deletion and modification; and, of course, selection of data including simple joins. The methods provided are NOT meant to provide all the capabilities available with a database. They are meant to make it very easy to accomplish the vast majority of operations typically needed using a very simple interface. Methods to submit arbitrary SQL query statements (anything that returns table data from the database) and SQL update statements (anything that modifies the database) are provided for the advanced user. Also, a method to obtain direct access to a Connection object for the database is provided for the expert JDBC programmer. @see PIRL.PVL pirl-2.3.8/PIRL/Database/tests/0000755000175000017500000000000012052546523015764 5ustar mathieumathieupirl-2.3.8/PIRL/Database/tests/Fields_Map_test.java0000644000175000017500000002233311742733571021702 0ustar mathieumathieu/* Fields_Map_test CVS ID: Fields_Map_test.java,v 1.4 2012/04/16 06:08:57 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Database.Fields_Map; import PIRL.Database.Database_Exception; import PIRL.Utilities.Checker; import java.util.Vector; public class Fields_Map_test { public static void main (String[] arguments) { System.out.println ("*** Fields_Map_test: " + Fields_Map.ID); Checker checker = new Checker (); if (arguments.length > 0 && arguments[0].startsWith ("-v")) checker.Verbose = true; Fields_Map map = null; Vector user_fields = null, actual_fields = null, record; try { // Constructor try { map = new Fields_Map (user_fields, actual_fields); System.out.println ("FAIL: null argument exception for map = new Fields_Map (null, null)"); if (checker.Verbose) System.out.println ("===> expected: IllegalArgumentException\n" +"===> obtained: No exception"); } catch (IllegalArgumentException exception) { checker.Checks_Passed++; System.out.println ("PASS: null argument exception for map = new Fields_Map (null, null)"); if (checker.Verbose) System.out.println ("===> expected: IllegalArgumentException\n" +"===> obtained: " + exception); } checker.Checks_Total++; user_fields = new Vector (4); user_fields.add ("u0-a1"); user_fields.add ("u1-a0"); user_fields.add ("u2-nomap"); user_fields.add ((String)null); actual_fields = new Vector (4); actual_fields.add ("u1-a0"); actual_fields.add ("u0-a1"); actual_fields.add ("a2-nomap"); actual_fields.add ((String)null); try { map = new Fields_Map (user_fields, actual_fields, false); checker.Checks_Passed++; System.out.println ("PASS: map = new Fields_Map (" + user_fields +", " + actual_fields + ", false)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: no exception"); } catch (IllegalArgumentException exception) { System.out.println ("FAIL: map = new Fields_Map (" + user_fields +", " + actual_fields + ", false)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; // index checker.Check ("map.index (0)", 1, map.index (0)); checker.Check ("map.index (1)", 0, map.index (1)); checker.Check ("map.index (2)", -1, map.index (2)); checker.Check ("map.index (3)", 3, map.index (3)); checker.Check ("map.index (4)", -1, map.index (4)); checker.Check ("map.index (\"" + (String)user_fields.get (0) + "\")", 1, map.index ((String)user_fields.get (0))); checker.Check ("map.index (\"" + (String)user_fields.get (1) + "\")", 0, map.index ((String)user_fields.get (1))); checker.Check ("map.index (\"" + (String)user_fields.get (2) + "\")", -1, map.index ((String)user_fields.get (2))); checker.Check ("map.index ((String)null)", 3, map.index ((String)null)); checker.Check ("map.index (\"foo\")", -1, map.index ("foo")); // entry record = new Vector (4); record.add ("First"); record.add ("Second"); record.add ("Third"); record.add ("Fourth"); checker.Check ("map.entry (" + record + ", 1)", (String)record.get (0), map.entry (record, 1)); checker.Check ("map.entry (" + record + ", 4)", (String)null, map.entry (record, 4)); checker.Check ("map.entry (" + record + ", \"" + (String)user_fields.get (0) + "\")", (String)record.get (1), map.entry (record, (String)user_fields.get (0))); checker.Check ("map.entry (" + record + ", (String)null)", (String)record.get (3), map.entry (record, (String)null)); // All/User_Fields record = new Vector (user_fields); checker.Check ("map.All_User_Fields ()", record, map.All_User_Fields ()); record.remove (2); checker.Check ("map.User_Fields ()", record, map.User_Fields ()); // All/Actual_Fields record = new Vector (actual_fields); checker.Check ("map.All_Actual_Fields ()", record, map.All_Actual_Fields ()); record.remove (2); checker.Check ("map.Actual_Fields ()", record, map.Actual_Fields ()); // Actual_to_User try { map.Actual_to_User (record); System.out.println ("FAIL: short record exception for map.Actual_to_User (" + record + ")"); if (checker.Verbose) System.out.println ("===> expected: IllegalArgumentException\n" +"===> obtained: No exception"); } catch (IllegalArgumentException exception) { checker.Checks_Passed++; System.out.println ("PASS: short record exception for map.Actual_to_User (" + record + ")"); if (checker.Verbose) System.out.println ("===> expected: IllegalArgumentException\n" +"===> obtained: " + exception); } checker.Checks_Total++; user_fields.set (2, (String)null); checker.Check ("map.Actual_to_User (" + actual_fields + ")", user_fields, map.Actual_to_User (actual_fields)); try { map.Confirm_Fields (user_fields); checker.Checks_Passed++; System.out.println ("PASS: map.Confirm_Fields (" + user_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: no exception"); } catch (Database_Exception exception) { System.out.println ("FAIL: map.Confirm_Fields (" + user_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; // Confirm_Fields try { map.Confirm_Fields (actual_fields); System.out.println ("FAIL: map.Confirm_Fields (" + actual_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: Database_Exception\n" +"===> obtained: no exception"); } catch (Database_Exception exception) { checker.Checks_Passed++; System.out.println ("PASS: map.Confirm_Fields (" + actual_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: Database_Exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; actual_fields.remove (2); try { map.Confirm_Fields (actual_fields); checker.Checks_Passed++; System.out.println ("PASS: map.Confirm_Fields (" + actual_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: no exception"); } catch (Database_Exception exception) { System.out.println ("FAIL: map.Confirm_Fields (" + actual_fields + ")"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; // Case_Sensitive - false user_fields = new Vector (2); user_fields.add ("U0-A1"); user_fields.add ("u1-a0"); actual_fields = new Vector (2); actual_fields.add ("u1-a0"); actual_fields.add ("u0-a1"); try { map = new Fields_Map (user_fields, actual_fields, false); checker.Checks_Passed++; System.out.println ("PASS: map = new Fields_Map (" + user_fields +", " + actual_fields + ", false)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: no exception"); } catch (IllegalArgumentException exception) { System.out.println ("FAIL: map = new Fields_Map (" + user_fields +", " + actual_fields + ", false)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; checker.Check ("map.index (\"" + (String)user_fields.get (0) + "\")", 1, map.index ((String)user_fields.get (0))); checker.Check ("map.index (\"" + (String)user_fields.get (1) + "\")", 0, map.index ((String)user_fields.get (1))); // Case_Sensitive - true try { map = new Fields_Map (user_fields, actual_fields, true); checker.Checks_Passed++; System.out.println ("PASS: map = new Fields_Map (" + user_fields +", " + actual_fields + ", true)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: no exception"); } catch (IllegalArgumentException exception) { System.out.println ("FAIL: map = new Fields_Map (" + user_fields +", " + actual_fields + ", true)"); if (checker.Verbose) System.out.println ("===> expected: no exception\n" +"===> obtained: " + exception); } checker.Checks_Total++; checker.Check ("map.index (\"" + (String)user_fields.get (0) + "\")", -1, map.index ((String)user_fields.get (0))); checker.Check ("map.index (\"" + (String)user_fields.get (1) + "\")", 0, map.index ((String)user_fields.get (1))); } catch (Exception exception) { System.out.println ("!!!!: Unexpected exception: " + exception); checker.Checks_Total++; } System.out.println ("Checks: " + checker.Checks_Total + '\n' +"Passed: " + checker.Checks_Passed); System.exit (checker.Checks_Total - checker.Checks_Passed); } } pirl-2.3.8/PIRL/Database/tests/Database_test.java0000644000175000017500000002576411742733571021416 0ustar mathieumathieu/* Database_test PIRL CVS ID: Database_test.java,v 1.11 2012/04/16 06:08:57 castalia Exp Unit tests for the PIRL.Database.Database class. Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Database.*; import PIRL.Configuration.*; import java.util.Vector; import java.util.Iterator; public class Database_test { private static String CONFIG_FILENAME = "Database.conf", TEST_CATALOG = "test", ALT_TEST_CATALOG = "TEST_CATALOG", TEST_TABLE = "TEST_TABLE", NEW_TEST_TABLE = "NEW_" + TEST_TABLE; public static void main (String[] args) { System.out.println ("*** Database_test ***\n" +" " + Database.ID + "\n"); String config_filename = null, server_name = null; boolean new_catalog = false, keep_tables = false, show_SQL = false; for (int count = 0; count < args.length; count++) { if (args[count].length () > 1 && args[count].charAt (0) == '-') { switch (args[count].toUpperCase ().charAt (1)) { case 'C': // Catalog create/delete. if (args[count].length () > 2 && args[count].toUpperCase ().charAt (2) == 'O') { // Config filename. if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') { System.out.println ("Missing configuration filename"); Usage (); } if (config_filename != null) { System.out.println ("Multiple configuration files -\n" + config_filename + '\n' + "and" + '\n' + args[count]); Usage (); } config_filename = args[count]; } else // Catalog create/delete. new_catalog = true; break; case 'S': if (args[count].toUpperCase ().equals ("-SQL")) { // SQL statements. show_SQL = true; break; } // Server name. if (++count == args.length || args[count].length () == 0 || args[count].charAt (0) == '-') { System.out.println ("Missing server name"); Usage (); } if (server_name != null) { System.out.println ("Multiple server names -\n" + server_name + '\n' + "and" + '\n' + args[count]); Usage (); } server_name = args[count]; break; case 'K': // Keep tables. keep_tables = true; break; default: System.out.println ("Unknown option: " + args[count]); case 'H': // Help. Usage (); } } else { System.out.println ("Unknown command line argument: " + args[count]); Usage (); } } if (config_filename == null) config_filename = CONFIG_FILENAME; if (new_catalog) TEST_CATALOG = ALT_TEST_CATALOG; try { // Create a new Configuration. Configuration configuration = new Configuration (config_filename), config = new Configuration (configuration); config.Case_Sensitive (false); config.Set_All ("password", "*****"); System.out.println (config.Description ()); // Create a new Database. Database database = new Database (configuration); // Connect to it. System.out.println ("--- database.Connect ();"); database.Connect (server_name); // Database description. System.out.println ("--- database.Description ();"); System.out.println (database.Description () + "\n"); if (show_SQL) { System.out.println ("--- database.Add_SQL_Listener (listner);"); database.Add_SQL_Listener (new SQL_Statements ()); } System.out.println ("--- database.Catalogs ();"); System.out.println (" Catalogs: " + database.Catalogs ()); if (new_catalog) { // Remove the existing catalog. System.out.println ("--- database.Delete (\"" + TEST_CATALOG + "\");"); database.Delete (TEST_CATALOG); // Create a new catalog. System.out.println ("--- database.Create (\"" + TEST_CATALOG + "\");"); database.Create (TEST_CATALOG); System.out.println ("--- database.Catalogs ();"); System.out.println (" Catalogs: " + database.Catalogs ()); } // Set the default catalog and table in the configuration. System.out.println ("--- Default " + Database.CATALOG + " set to \"" + TEST_CATALOG + "\" in the Database Configuration."); database.Data_Port ().Configuration ().Set (Database.CATALOG, TEST_CATALOG); System.out.println ("--- Default " + Database.TABLE + " set to \"" + TEST_TABLE + "\" in the Database Configuration."); database.Data_Port ().Configuration ().Set (Database.TABLE, TEST_TABLE); // Remove the existing tables. System.out.println ("--- database.Delete (\"" + TEST_TABLE + "\", (Vector)null);"); try {database.Delete (TEST_TABLE, (Vector)null);} catch (Database_Exception exception) { System.out.println (" Delete failed (table does not exist?)"); } System.out.println ("--- database.Delete (\"" + NEW_TEST_TABLE + "\", (Vector)null);"); try {database.Delete (NEW_TEST_TABLE, (Vector)null);} catch (Database_Exception exception) { System.out.println (" Delete failed (table does not exist?)"); } // Create a new table. Vector fields = new Vector (), types = new Vector (); fields.add ("ID"); types.add ("SERIAL" + " Not NULL" + " Primary Key"); fields.add ("Name"); types.add ("VARCHAR(64)"); fields.add ("Value"); types.add ("FLOAT"); System.out.println ("--- database.Create (\"" + TEST_TABLE + "\", \"" + fields + "\", \"" + types + "\");"); database.Create (TEST_TABLE, fields, types); System.out.println (" Catalogs: " + database.Catalogs ()); System.out.println (" Tables in \"" + TEST_CATALOG + "\": " + database.Tables (TEST_CATALOG)); // Insert data into the table. Vector insert_fields = new Vector (fields); // Don't include the auto-increment field. insert_fields.remove (0); Vector values = new Vector (insert_fields.size ()); values.add (""); values.add (""); for (int count = 0; count < 10; count++) { values.set (0, "Name_" + count); values.set (1, new Float ((float)count + ((float)count / 10) + .01)); System.out.println ("--- database.Insert (\"" + TEST_TABLE + "\", \"" + insert_fields + "\", \"" + values + "\");"); database.Insert (TEST_TABLE, insert_fields, values); } // Describe the table contents. System.out.println ("--- database.Contents (\"" + TEST_CATALOG + "\", \"" + TEST_TABLE + "\");"); System.out.println (database.Contents (TEST_CATALOG, TEST_TABLE)); System.out.println ("--- database.Keys (\"" + TEST_TABLE + "\");"); System.out.println (database.Keys (TEST_TABLE)); // Select the entire table. System.out.println ("--- database.Select ();"); Print_Table (database.Select ()); // Rename the table. System.out.println ("--- database.Rename (\"" + TEST_TABLE + "\", \"" + NEW_TEST_TABLE + "\");"); database.Rename (TEST_TABLE, NEW_TEST_TABLE); System.out.println (" Tables in \"" + TEST_CATALOG + "\": " + database.Tables (TEST_CATALOG)); // Recreate the original table. System.out.println ("--- database.Create (\"" + TEST_TABLE + "\", \"" + fields + "\", \"" + types + "\");"); database.Create (TEST_TABLE, fields, types); System.out.println (" Tables in \"" + TEST_CATALOG + "\": " + database.Tables (TEST_CATALOG)); // Copy the data. System.out.println ("--- database.Insert (\"" + TEST_TABLE + "\", database.Select (\"" + NEW_TEST_TABLE + "\"));"); System.out.println (" " + database.Insert (TEST_TABLE, database.Select (NEW_TEST_TABLE)) + " records inserted."); // Select the entire table. System.out.println ("--- database.Select ();"); Print_Table (database.Select ()); // Delete a selected record. System.out.println ("--- database.Delete (\"" + TEST_TABLE + "\", \"Name='Name_6'\");"); System.out.println (" " + database.Delete (TEST_TABLE, "Name='Name_6'") + " record deleted."); Print_Table (database.Select ()); // Add new fields. fields.clear (); types.clear (); fields.add ("Switch"); types.add ("VARCHAR(20) DEFAULT 'OFF' Not NULL"); fields.add ("Last"); types.add ("TIMESTAMP"); System.out.println ("--- database.Create (\"" + TEST_TABLE + "\", \"" + fields + "\", \"" + types + "\");"); database.Create (TEST_TABLE, fields, types); Print_Table (database.Select ()); // Update selected records. fields.clear (); values.clear (); fields.add ("Name"); values.add ("MOD"); fields.add ("Value"); values.add ("3.1459"); System.out.println ("--- database.Update (\"" + TEST_TABLE + "\", \"" + fields + "\", \"" + values + "\", \"ID>4 AND ID<8\");"); System.out.println (" " + database.Update (TEST_TABLE, fields, values, "ID>4 AND ID<8") + " records updated."); Print_Table (database.Select ()); // Delete a field. fields.clear (); fields.add ("Switch"); System.out.println ("--- database.Delete (\"" + TEST_TABLE + "\", \"" + fields + "\");"); database.Delete (TEST_TABLE, fields); Print_Table (database.Select ()); if (! keep_tables) { // Delete the tables. System.out.println ("--- database.Delete (\"" + TEST_TABLE + "\", (Vector)null);"); database.Delete (TEST_TABLE, (Vector)null); System.out.println ("--- database.Delete (\"" + NEW_TEST_TABLE + "\", (Vector)null);"); database.Delete (NEW_TEST_TABLE, (Vector)null); System.out.println (" Tables in \"" + TEST_CATALOG + "\": " + database.Tables (TEST_CATALOG)); if (new_catalog) { // Delete the catalog. System.out.println ("--- database.Delete (\"" + TEST_CATALOG + "\");"); database.Delete (TEST_CATALOG); System.out.println (" Catalogs: " + database.Catalogs ()); } } // Disconnect. System.out.println ("--- database.Disconnect ();"); database.Disconnect (); } catch (Exception exception) { System.out.println (exception.getMessage () + "\n"); System.exit (2); } System.exit (0); } public static void Print_Table ( Vector table ) { Iterator table_list = table.iterator (); while (table_list.hasNext ()) System.out.println (table_list.next ()); System.out.println (); } public static class SQL_Statements implements SQL_Listener { public void SQL_Statement ( String SQL ) {System.out.println (SQL);} } public static void Usage () { System.out.println ("Usage: Database_test []\n" +" Options -\n" +" -Catalog\n" +" Create and delete a \"" + ALT_TEST_CATALOG + "\" catalog.\n" +" Default: Use an existing \"" + TEST_CATALOG + "\" catalog.\n" +" -Keep\n" +" Default: Delete the \"" + TEST_TABLE + "\" and \"" + NEW_TEST_TABLE + "\" tables after use.\n" +" -COnfig_file \n" +" Default: " + CONFIG_FILENAME + '\n' +" -Server \n" +" Default: First Server in the config file.\n" +" -Help\n"); System.exit (1); } } pirl-2.3.8/PIRL/Database/tests/Update_DB_test.conf0000644000175000017500000000033710065641554021466 0ustar mathieumathieu# Update_DB Configuration # # PIRL CVS ID: Update_DB_test.conf,v 1.1 2004/06/21 20:22:04 castalia Exp Server = pirldb Group = pirldb Type = MySQL Host = pirldb.lpl.arizona.edu User = public Password = public End_Group pirl-2.3.8/PIRL/Database/tests/Database.conf0000644000175000017500000000101011015163763020326 0ustar mathieumathieu# Database.conf # PIRL CVS ID: Database.conf,v 1.4 2008/05/22 03:26:43 castalia Exp # # Modify for testing at your site. Server = local Group = local Host = localhost Type = MySQL User = public Password = public /* MySQL version 5 changed the behaviour of TIMESTAMP fields. Unless the following MySQL driver option is specified the Database_test classs will throw and exception immediately after the Last (TIMESTAMP) field is added to the TEST_TABLE. */ zeroDateTimeBehavior = convertToNull End_Group pirl-2.3.8/PIRL/Database/tests/Update_DB_test0000755000175000017500000003032011742733571020544 0ustar mathieumathieu#!/bin/perl # Update_DB_test # # PIRL CVS ID: Update_DB_test,v 1.12 2012/04/16 06:08:57 castalia Exp # # # This procedure performs unit tests for the Update_DB Java application. # # The test is performed using a local MySQL database. Access to the # database must be configured in the Update_DB_test.conf file as well as # the for the mysql client application used to create the test table and # confirm the database operations. # # The test.update_db table is intially created anew. The user must have # privileges to create and drop this table, including the creation of the # containing catalog if it doesn't exist. # # A sequence of recrod insert tests is followed by a sequence of record # update tests. At the end of the tests the test table is dropped, but # not the containing catalog. # # # Copyright (C) 2003-2012 Arizona Board of Regents on behalf of the # Planetary Image Research Laboratory, Lunar and Planetary Laboratory at # the University of Arizona. # # This file is part of the PIRL Java Packages. # # The PIRL Java Packages are free software; you can redistribute them # and/or modify them under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # The PIRL Java Packages are distributed in the hope that they will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # use English; print "--- Update_DB test ----\n"; while ($option = shift @ARGV) { if ($option =~ /^-[Vv]/) { # Verbose. $Verbose = 1; next; } if ($option =~ /^-[Kk]/) { # Keep. $Keep = 1; next; } print "Uknown option: $option\n"; print "Usage: Update_DB_test [-Verbose]\n"; exit (1); } # Update_DB configuration file. my $Config_File = "./Update_DB_test.conf"; if (! -r $Config_File) { print "The required $Config_File configuration file is missing!\n"; exit (1); } # Database client for submitting SQL statements. my $SQL_Tool = "mysql"; # Database catalog where the test table will be located. my $Catalog = "test"; # Database table on which the test will be performed. my $Table = "$Catalog.update_db"; # Java runtime environment. my $Java_Tool = "java"; my $classpath; # Provide the classpath for the database driver. $classpath = "/opt/java/mysql_connector" unless ($classpath = $ENV{"CLASSPATH"}); # Add the classpath for the Update_DB class. $Java_Tool .= " -classpath ../../..:$classpath"; # Add the Update_DB class. $Java_Tool .= " PIRL.Database.Update_DB"; # Add the Update_DB verbose option. $Java_Tool .= " -verbose" if $Verbose; # Add the Update_DB configuration file. $Java_Tool .= " -configure $Config_File"; #------------------------------------------------------------------------------- # Create the test table my $Create_DB; $Exit_Status; print "--- Create the $Table table.\n"; $Create_DB = "$SQL_Tool -e "; $Create_DB .= "'CREATE DATABASE IF NOT EXISTS $Catalog;'"; print "$Create_DB\n" if $Verbose; if ($Exit_Status = system ("$Create_DB") >> 8) { print "*** FAIL: You may not have the database privilege to do this.\n"; exit (1); } $Create_DB = "$SQL_Tool -e "; $Create_DB .= "'DROP TABLE IF EXISTS $Table;'"; print "$Create_DB\n" if $Verbose; if ($Exit_Status = system ("$Create_DB") >> 8) { print "*** FAIL: You may not have the database privilege to do this.\n"; exit (1); } $Create_DB = "$SQL_Tool -e "; $Create_DB .= "'CREATE TABLE $Table ("; $Create_DB .= "id INT AUTO_INCREMENT PRIMARY KEY,"; $Create_DB .= "text_1 TEXT,"; $Create_DB .= "text_2 TEXT,"; $Create_DB .= "int_1 INT,"; $Create_DB .= "int_2 INT);'"; print "$Create_DB\n" if $Verbose; if ($Exit_Status = system ("$Create_DB") >> 8) { print "**** FAIL: You may not have the database privilege to do this.\n"; exit (1); } #------------------------------------------------------------------------------- # Insert print "\n>>> Insert\n"; my $Update_DB; $Expected; $Got; $Total_Tests = 0; $Passed_Tests = 0; print "\n--- Insert one record, all fields (except auto-increment id).\n"; $Update_DB = "$Java_Tool"; $Update_DB .= " -Reference $Table"; $Update_DB .= " text_1=\"Value 1\""; $Update_DB .= " text_2=\"Value 2\""; $Update_DB .= " int_1=1000"; $Update_DB .= " int_2=2000"; print "$Update_DB\n" if $Verbose; system ("$Update_DB"); &Done (1) if ! &Check_Status (1); $Expected = <>> Update\n"; print "\n--- Update the first record, all fields.\n"; $Update_DB = "$Java_Tool"; $Update_DB .= " -Reference $Table:id=1"; $Update_DB .= " text_1=\"Field 1\""; $Update_DB .= " text_2=\"Field 2\""; $Update_DB .= " int_1=4001"; $Update_DB .= " int_2=20001"; print "$Update_DB\n" if $Verbose; system ("$Update_DB"); &Check_Status (1); $Expected = <>> Arguments file\n"; print "\n--- Insert from arguments file.\n"; if (open (ARGUMENTS, "> Update_DB.arguments")) { print ARGUMENTS "-Table $Table" . " text_1=\"Value 1\"" . " text_2=\"Value 2\"" . " int_1=1000" . " int_2=2000" . "\n"; close ARGUMENTS; $Update_DB = "$Java_Tool"; $Update_DB .= ' @Update_DB.arguments'; print "$Update_DB\n" if $Verbose; system ("$Update_DB"); &Done (1) if ! &Check_Status (1); $Expected = <>> Delete\n"; print "\n--- Delete a record.\n"; $Update_DB = "$Java_Tool"; $Update_DB .= " -Delete"; $Update_DB .= " -Reference $Table:id=5"; print "$Update_DB\n" if $Verbose; system ("$Update_DB"); &Check_Status (1); $Expected = <> 8; my $match = ($status == $expected); print "*** ", ($match ? "PASS" : "FAIL"), ": exit status $status, expected $expected.\n"; $Total_Tests++; $Passed_Tests++ if $match; return $match; } sub Check_Expected { my $match = $Got eq $Expected; print "*** ", ($match ? "PASS" : "FAIL"), ": table contents\n"; print " expected:\n$Expected", " got:\n$Got" if $Verbose; $Total_Tests++; $Passed_Tests++ if $match; return $match; } sub Done { my ($status) = @_; print "\n", $Passed_Tests, " test", ($Passed_Tests == 1 ? "" : "s"), " passed.\n", $Total_Tests, " test", ($Total_Tests == 1 ? "" : "s"), " tried.\n"; if (! $Keep) { # Remove the test table $Create_DB = "$SQL_Tool -e "; $Create_DB .= "'DROP TABLE IF EXISTS $Table;'"; print "\n--- Database cleanup\n$Create_DB\n" if $Verbose; if ($Exit_Status = system ("$Create_DB") >> 8) { print "*** FAIL: You may not have the database privilege to do this."; exit (1); } } exit ($status); } pirl-2.3.8/PIRL/Database/tests/Connect_test.java0000644000175000017500000000561611742733571021275 0ustar mathieumathieu/* Connect_test Simply constructs a Database using the default Database.conf configuration, then a push of a button will Connect or Disconnect. A Database construction exception is reported to stdout. Connect/Disconnect exceptions are reported in a dialog box. CVS ID: Connect_test.java,v 1.3 2012/04/16 06:08:57 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ import PIRL.Database.*; import PIRL.Viewers.*; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.Dimension; public class Connect_test { private static Database The_Database; private static boolean Connected = false; private static JButton Connection_Button; public static void main (String[] arguments) { final JFrame frame = new JFrame ("Database Connection Test"); frame.addWindowListener (new WindowAdapter () {public void windowClosing (WindowEvent e) {System.exit (0);}}); JPanel content = new JPanel (); frame.setContentPane (content); Connection_Button = new JButton ("Connect"); Connection_Button.addActionListener (new ActionListener () {public void actionPerformed (ActionEvent e) { Connection_Action (); }}); content.add (Connection_Button); // Create a new Database. try {The_Database = new Database ();} catch (Database_Exception exception) {System.out.println ("Unable construct a Database object.\n" + exception.getMessage ());} content.setPreferredSize (new Dimension (150, 50)); frame.pack (); frame.setVisible (true); } private static void Connection_Action () { try { if (Connected) { The_Database.Disconnect (); Connection_Button.setText ("Connect"); Connected = false; } else { The_Database.Connect (); Connection_Button.setText ("Disconnect"); Connected = true; } } catch (Exception exception) { Dialog_Box.Error ("Unable to " + (Connected ? "dis" : "") + "connect\n\n" + exception.getMessage ()); } } } pirl-2.3.8/PIRL/Database/tests/Makefile0000644000175000017500000000053711103723523017422 0ustar mathieumathieu# Makefile for Java classes # PIRL CVS ID: Makefile,v 1.5 2008/11/04 01:37:23 castalia Exp JPATH ?= .:../../.. JC ?= javac .SUFFIXES: .java .class .java.class: ${JC} -classpath ${JPATH} ${JFLAGS} $< CLASSES = Fields_Map_test.class \ Database_test.class \ Connect_test.class all: classes classes: ${CLASSES} clean: rm -f *.class pirl-2.3.8/PIRL/Database/Update_DB.java0000644000175000017500000011352311742733571017267 0ustar mathieumathieu/* Update_DB PIRL CVS ID: Update_DB.java,v 3.27 2012/04/16 06:08:57 castalia Exp Copyright (C) 2003-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Configuration.Configuration; import PIRL.Configuration.Configuration_Exception; import PIRL.Strings.String_Buffer; import PIRL.Strings.Words; import PIRL.Strings.String_Utilities; import java.util.Vector; import java.util.Stack; import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.FileNotFoundException; /** Update_DB updates an existing database table record or inserts a new record in a table.

Database update operations are based on references to one or more tables to be affected plus, for each table reference, a list of table field names paired with the values that they are to receive. By attaching a key (SQL conditional expression) to a table reference one or more records from the table can be selected to have the specified field values updated. Without a key the field values will be put in a new record inserted at the end of the table.

An application {@link #main(String[]) main} is provided using a {@link #Usage(boolean) command line user inteface} that will invoke the update operations of this class.

@author Bradford Castalia, Christian Schaller - UA/PIRL @version 3.27 @see PIRL.Database @see PIRL.Configuration */ public class Update_DB { /** The Class identification. */ public static final String ID = "PIRL.Database.Update_DB (3.27 2012/04/16 06:08:57)"; /** The default Configuration filename. */ public static final String DEFAULT_CONFIGURATION_FILENAME = "Database.conf"; /** Key delimiter for table/field references. */ public static final char KEY_DELIMITER = ':'; /** Delimiter for input field assignments' values. */ public static final char VALUE_DELIMITER = '='; /** The Database to be updated. */ protected Database The_Database = null; /** Flag for verbose operation. */ protected boolean Verbose = false; /** Flag for limiting updates to one record. */ protected boolean Update_One_Only = false; /** Flag for ignoring multiple updates, rather than throwing an exception, when {@link #Update_One_Only(boolean) Update_One_Only} is enabled. */ protected boolean Ignore_Multiple_Updates = false; /** Flag for {@link #Delete(boolean) deleting} selected records instead of updating them. */ protected boolean Delete = false; /** Application exit status when invalid command line syntax was encounterd. */ public static final int EXIT_INVALID_COMMAND_LINE_SYNTAX = 255; /** Application exit status when there is a Configuration file problem. */ public static final int EXIT_CONFIGURATION_PROBLEM = 254; /** Application exit status when there is a problem accessing the Database. */ public static final int EXIT_DATABASE_ERROR = 253; /** Application exit status when an illegal argument is encountered. */ public static final int EXIT_ILLEGAL_ARGUMENT = 252; /** Application exit status when an I/O error occured on an arguments file. */ public static final int EXIT_IO_ERROR = 251; /** Application exit status limit indicating the number of records affected. This is the maxium number of records that can be specified through the 8-bit exit status value. */ public static final int EXIT_STATUS_LIMIT = 250; // Filesystem pathname separator. private static final char FILE_SEPARATOR = System.getProperty ("file.separator").charAt (0); /** System new line sequence. */ protected static final String NL = Database.NL; // Debug control private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_DATABASE = 1 << 1, DEBUG_MAIN = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs an Update_DB object from a Configuration with a connection to a database server.

The Configuration object is expected to contain all the necessary information Update_DB needs to connect to the database.

@param database The Database to be updated. */ public Update_DB ( Database database ) { if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Update_DB"); The_Database = database; if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Update_DB"); } // La construccion de un PVL_to_DB vacio se prohibe. private Update_DB () {} /*============================================================================== Accessors */ /** Enables or disables verbose mode.

When verbose mode is enabled all operations are logged. Verbose mode is disabled by default.

@param enable true to enable the mode, false to disable. @return This Update_DB object. */ public Update_DB Verbose ( boolean enable ) {Verbose = enable; return this;} /** Tests if verbose mode is enabled.

@return true if verbose mode is enabled, false if disabled. @see #Verbose(boolean) */ public boolean Verbose () {return Verbose;} /** Enables or disables the single record update only rule.

When enabled only one record will be allowed to be updated for any table reference/field assignments set. The table reference key is first used to select the records specified by the key and, if more that one record is selected, the update operation is aborted. When disabled (the default) more than one record may be updated by each update set.

N.B.: Enforcing the single record update rule by doing an initial select is not very efficient, and would be better implemented using transaction rollbacks. However, since not all databases support transaction rollbacks the select first method is guaranteed to work with all databases. The initial select is not done if the single record update rule is disabled.

@param enable true to enable the rule, false to disable. @return This Update_DB object. @see #Ignore_Multiple_Updates(boolean) */ public Update_DB Update_One_Only ( boolean enable ) {Update_One_Only = enable; return this;} /** Tests if the single record update rule is enabled.

@return true if the rule is enabled, false if disabled. @see #Update_One_Only(boolean) */ public boolean Update_One_Only () {return Update_One_Only;} /** Enables or disables ignoring multiple record updates, rather than throwing an exception, when only one record is allowed.

When enabled and {@link #Update_One_Only(boolean) Update_One_Only} is also enabled if a table reference key selects more than one record the update will not be done. When disabled and Update_One_Only is enabled an IllegalArgumentException will be thrown with a message describing the situation.

@param enable If true multiple record updates will be ignored when only one record is allowed; otherwise an IllegalArgumentException will be thrown in this case. @return This Update_DB object. @see #Update_One_Only(boolean) */ public Update_DB Ignore_Multiple_Updates ( boolean enable ) {Ignore_Multiple_Updates = enable; return this;} /** Tests if multiple record updates will be ignored when only one record is allowed.

@return true if multiple record updates will be ignored when only one record is allowed; otherwise an IllegalArgumentException will be thrown in this case. @see #Ignore_Multiple_Updates(boolean) */ public boolean Ignore_Multiple_Updates () {return Ignore_Multiple_Updates;} /** Enables or disables record deletion.

When delete is enabled and the table reference includes a key that selects one or more records, the records will be deleted rather than updated. If {@link #Update_One_Only(boolean) Update_One_Only} is also enabled the check for a single record selection will be applied.

If no key is provided

@param enable If true selected records will be deleted; otherwise selected records will be updated. @return This Update_DB object. */ public Update_DB Delete ( boolean enable ) {Delete = enable; return this;} /** Tests if record deletion is enabled.

@return true if record deletion is enabled; false otherwise. @see #Delete(boolean) */ public boolean Delete () {return Delete;} /** Gets the Database being updated.

@return The Database currently being used. */ public Database Database () {return The_Database;} /*============================================================================== Database Operations */ /** Updates the database using a table reference and field assignments.

A key is split off from the table_reference (on the first occurance of a {@link #KEY_DELIMITER KEY_DELIMITER} character), if possible. The field_assignments are split (on the first occurance of a {@link #VALUE_DELIMITER VALUE_DELIMITER} character) into a list of field names and their corresponding values. Then the alternate {@link #Update_Database(String, String, Vector, Vector) Update_Database} method is invoked.

@param table_reference The table reference, with optional key. @param field_assignments The Vector of fields with assigned values. @return The number of records affected. @throws IllegalArgumentException If the table_reference is null, or a field assignment is missing either a name or value. @see #Update_Database(String, String, Vector, Vector) */ public int Update_Database ( String table_reference, Vector field_assignments ) throws Database_Exception, IllegalArgumentException { if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println (">>> Update_Database:" + NL +" table_reference - " + table_reference + NL +" field_assignments - " + field_assignments + NL); String table, key, assignment, field_name, field_value; if (table_reference == null) throw new IllegalArgumentException (Error_Message ( "Missing table reference for Update_Database.")); if ((table = String_Utilities.Preceeding (table_reference, KEY_DELIMITER)) == null) table = table_reference; key = String_Utilities.Following (table_reference, KEY_DELIMITER); Vector field_names = new Vector (), field_values = new Vector (); int index = -1, size = field_assignments.size (); while (++index < size) { assignment = field_assignments.get (index); if ((field_name = String_Utilities.Preceeding (assignment, VALUE_DELIMITER)) == null) throw new IllegalArgumentException (Error_Message ( "Missing field name in " + assignment)); if ((field_value = String_Utilities.Following (assignment, VALUE_DELIMITER)) == null) throw new IllegalArgumentException (Error_Message ( "Missing field value in " + assignment)); field_names.add (field_name); field_values.add (field_value); } int records = Update_Database (table, key, field_names, field_values); if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Update_Database (table_reference): " + records + " records"); return records; } /** Updates the database.

The details of for the arguments is described in the command line {@link #Usage(boolean) Usage}..

@param table_reference A Database table reference, without any key. @param key The SQL conditional to select records for an update operation; null if an insert operation is to be done. @param field_names The names of the fields into which data will be placed. @param field_values The values of the fields for the corresponding field_names. @return The number of records affected. @throws IllegalArgumentException If {@link #Update_One_Only(boolean) only one} record is to be updated (not inserted) and the key selects more than one record. Also, if the table_reference is null, or a field assignment is missing either a name or value. @throws Database_Exception If there is a problem accessing the database. @see Database#Insert(String, Vector, Vector) @see Database#Update(String, Vector, Vector, String) */ public int Update_Database ( String table_reference, String key, Vector field_names, Vector field_values ) throws Database_Exception, IllegalArgumentException { String full_table_reference = The_Database.Table_Reference (The_Database.Catalog_Name (table_reference), The_Database.Table_Name (table_reference)); if ((DEBUG & DEBUG_DATABASE) != 0) { System.out.println (">>> Update_Database" + NL +" table_reference - " + table_reference + " (" + full_table_reference + ')' + NL +" key - " + key + NL +" field names - " + field_names + NL +" field values - " + field_values); Verbose = true; } int records = 0; if (key == null) // INSERT { if (Delete) { if (Verbose) System.out.println ("For " + Database_Reference () + NL +"No key for selecting records to delete."); if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Update_Database: 0"); return 0; } records = The_Database.Insert (table_reference, field_names, field_values); if (Verbose) System.out.println ("For " + Database_Reference () + NL + records + " record" + ((records == 1) ? "" : "s") + " inserted in table " + full_table_reference + " with fields:"); } else // UPDATE { if (Update_One_Only) { Vector> selected_records = The_Database.Select (table_reference, key); if ((records = selected_records.size () - 1) != 1) { String report = "For " + Database_Reference () + NL +"key " + key + NL +"selected " + records + " record" + ((records == 1) ? "" : "s") +" from the " + full_table_reference + " table" + NL +"but only one record update is allowed." + NL +"No " + (Delete ? "delete" : "update") + " performed."; if (! Ignore_Multiple_Updates) throw new IllegalArgumentException (Error_Message (report)); if (Verbose) System.out.println (report + NL); if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Update_Database: 0"); return 0; } } if (Delete) records = The_Database.Delete (table_reference, key); else records = The_Database.Update (table_reference, field_names, field_values, key); if (Verbose) System.out.println ("For " + Database_Reference () + NL +"using key " + key + NL + records + " record" + ((records == 1) ? "" : "s") + (Delete ? " deleted" : " updated") + " in the " + full_table_reference + " table" + (Delete ? "." : " with fields:")); } if (Verbose && ! Delete) { int index = -1, size = field_names.size (); while (++index < size) System.out.println (" " + field_names.get (index) + " = " + field_values.get (index)); } if ((DEBUG & DEBUG_DATABASE) != 0) System.out.println ("<<< Update_Database: " + records + " records"); return records; } /*============================================================================== Utilties */ /** Produces an error message with the application's ID.

The message string will be prepended with the class (@link #ID ID} and a new-line, if the ID is not already present in the message. If the message is null, it will be replaced with the ID.

@param message The message String. @return The modified message String. */ private static String Error_Message ( String message ) { if (message == null) message = ID; else if (message.indexOf (ID) < 0) message = ID + NL + message; return message; } /** Produces a Database_Exception containing a message.

The message will be prepended with a description of the Database from the Configuration and then modified by the {@link #Error_Message(String) Error_Message} method before it is used to construct a new Database_Exception.

@param message The message String. @return A Database_Exception. */ private Database_Exception Database_Error ( String message ) { return new Database_Exception (Error_Message ("Problem accessing " + Database_Reference () + ":" + NL + message)); } /** Produces a one line description of the Database.

The description is of the form:

TYPE database on host HOST

The Configuration is used to obtain the {@link Database#TYPE TYPE} and {@link Database#HOST HOST} parameters.

@return A database reference string. */ private String Database_Reference () { if (The_Database == null || The_Database.Data_Port () == null) return "No Database"; return The_Database.Data_Port ().Configuration ().Get_One (Database.TYPE) + " database on host " + The_Database.Data_Port ().Configuration ().Get_One (Configuration.HOST); } /*============================================================================== Application main */ /** Runs the Update_DB application.

N.B.: If a Database connection is establshed it is always Disconnected before the application exits for any reason.

Exit status values:

>=0 - Success: the number of records affected (to a maximum of 250).
255 - Invalid command line syntax.
254 - Configuration problem.
253 - Database access error.
252 - An illegal argument was encountered.
251 - An I/O error occured while attempting to read an argument file.

@param args The {@link #Usage(boolean) command line} arguments. */ public static void main ( String[] args ) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println ("*** " + ID + " ***"); if (args.length == 0) Usage (true); for (int count = args.length; --count >= 0;) if (args[count].toUpperCase ().startsWith ("-H")) Usage (true); Update_DB update_DB = null; String configuration_filename = null, database_server = null, table_reference = null, name; Vector field_assignments = new Vector (); int records = 0; boolean verbose = false, update_one_only = false, ignore_multiple_updates = false, delete = false; // Argument processing. String arguments[] = args; String arguments_source = null; BufferedReader arguments_reader = null; Words argument_words = new Words ().Delimit_at_Quote (false); Stack sources_stack = new Stack (); Stack arguments_stack = new Stack (); Stack reader_stack = new Stack (); Stack index_stack = new Stack (); int index = 0; while (index < arguments.length) { // Start a new update set. table_reference = null; field_assignments.clear (); Arguments: while (true) { if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Argument " + index + ": " + arguments[index]); if (arguments[index].length () > 1 && arguments[index].charAt (0) == '-') { switch (arguments[index].charAt (1)) { case 'C': // -Configuration case 'c': if (++index == arguments.length || arguments[index].charAt (0) == '-') { // Use the default configuration file. name = DEFAULT_CONFIGURATION_FILENAME; --index; } else name = arguments[index]; if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Config file: " + name); if (configuration_filename == null) configuration_filename = name; else if (! name.equals (configuration_filename)) { System.out.println ("Additional configuration file " + name + " can not be used;" + NL + configuration_filename + " is already in use."); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } break; case 'N': // -No_delete case 'n': delete = false; break; case 'D': case 'd': if (arguments[index].length () > 2 && Character.toUpperCase (arguments[index].charAt(2)) == 'E') { // -Delete delete = true; break; } // -Database case 'S': // -Server case 's': if (++index == arguments.length || arguments[index].charAt (0) == '-') { System.err.println (ID + NL + "Missing database server name for argument " + arguments[--index] + '.'); Disconnect (update_DB); Usage (verbose); } name = arguments[index]; if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Database server: " + name); if (database_server == null) { if (update_DB != null) { System.out.println ("Additional database server name " + name + " can not be used;" + NL + "configuration default is already in use."); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } database_server = name; } else if (! name .equals (database_server)) { System.out.println ("Additional database server name " + name + " can not be used;" + NL + database_server + " is already in use."); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } break; case 'T': // -Table case 't': case 'R': // -Reference case 'r': if (table_reference != null) // Process the previous table and fields set. break Arguments; if (++index == arguments.length || arguments[index].charAt (0) == '-') { System.err.println (ID + NL + "Missing table reference for argument " + arguments[--index] + '.'); Disconnect (update_DB); Usage (verbose); } table_reference = arguments[index]; if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Table reference: " + table_reference); break; case '1': // -1, Only single record updates allowd. update_one_only = true; if (arguments[index].length () > 2 && arguments[index].charAt(2) == '+') ignore_multiple_updates = true; else ignore_multiple_updates = false; break; case '+': // -+, Multiple updates allowed. update_one_only = false; break; case 'V': // -Verbose case 'v': verbose = true; break; case 'Q': // -Quiet case 'q': verbose = false; break; default: System.err.println (ID + NL + "Unrecognized switch: " + arguments[index]); } } else { if (arguments[index].length () > 0 && arguments[index].charAt (0) == '@') { // Push the current arguments context. sources_stack.push (arguments_source); reader_stack.push (arguments_reader); arguments_stack.push (arguments); index_stack.push (new Integer (index)); if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Argument context pushed."); // Arguments source. if (arguments[index].length () == 1) { System.err.println (ID + NL + "Missing filename for '@' argument."); Disconnect (update_DB); Usage (verbose); } arguments_source = arguments[index].substring (1); File file = null; if (! arguments_source.equals ("-")) { file = new File (arguments_source); if (! file.exists ()) { System.err.println (ID + NL + "No such file: " + arguments[index]); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } if (! file.isFile ()) { System.err.println (ID + NL + "Not a regular file: " + arguments[index]); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } if (! file.canRead ()) { System.err.println (ID + NL + "Can not read file: " + arguments[index]); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } try {arguments_source = file.getCanonicalPath ();} catch (IOException exception) { System.err.println (ID + NL + "Unable to obtain cononical pathname for file: " + arguments_source + NL + exception.getMessage ()); Disconnect (update_DB); System.exit (EXIT_IO_ERROR); } } if (sources_stack.contains (arguments_source)) { System.err.println (ID + NL + "Circular reference file: " + arguments[index] + NL + " " + arguments_source); while (! sources_stack.isEmpty ()) System.err.println ("from " + sources_stack.pop ()); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } // Arguments reader. if (file == null) // stdin. arguments_reader = new BufferedReader (new InputStreamReader (System.in)); else { try {arguments_reader = new BufferedReader (new FileReader (file));} catch (FileNotFoundException exception) { // Shouldn't happen. System.err.println (ID + NL + "Could not access file: " + arguments[index]); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } } // Force arguments refresh from the new reader. index = arguments.length; } else // Field assignment. field_assignments.add (arguments[index]); } while (++index >= arguments.length) { if (arguments_reader != null) { String line = null; Vector words = new Vector (); while (words.isEmpty ()) { try {line = arguments_reader.readLine ();} catch (IOException exception) { System.err.println (ID + NL + "Read failed from file: " + arguments_source + NL + exception.getMessage ()); Disconnect (update_DB); System.exit (EXIT_IO_ERROR); } if (line == null) { // EOF. if (sources_stack.isEmpty ()) // End of arguments. break Arguments; // Pop the previous arguments context. arguments_source = sources_stack.pop (); arguments_reader = reader_stack.pop (); arguments = arguments_stack.pop (); index = index_stack.pop ().intValue (); if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Arguments context popped to " + arguments_source); break; } if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Line: " + line); words = argument_words.Characters (line).Split (); } if (line != null) { // Refresh the arguments array. arguments = new String[words.size ()]; if ((DEBUG & DEBUG_MAIN) != 0) System.out.print (" New arguments: "); for (index = 0; index < words.size (); index++) { String_Buffer word = new String_Buffer (words.get (index)); arguments[index] = word.clean ('"').toString (); if ((DEBUG & DEBUG_MAIN) != 0) System.out.print (arguments[index] + (((index + 1) == words.size ()) ? "" : ", ")); } if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (); index = 0; break; } } else // End of arguments. break Arguments; } } // Process the current table and fields set. if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" Processing update set."); if (table_reference == null) { System.err.println (ID + NL + "No table reference."); Disconnect (update_DB); Usage (verbose); } if (field_assignments.size () == 0 && ! delete) { System.err.println (ID + NL + "Table reference " + table_reference + NL +" has no field assignment."); if (update_DB != null || index < arguments.length || arguments_reader != null) // Not the only table and fields set. continue; Disconnect (update_DB); Usage (verbose); } if (update_DB == null) { // Construct the Configuration. Configuration configuration = null; if (configuration_filename == null) configuration_filename = DEFAULT_CONFIGURATION_FILENAME; try {configuration = new Configuration (configuration_filename);} catch (Configuration_Exception exception) { System.err.println (ID + NL + "Unable to construct a Configuration with file " + configuration_filename + "." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } catch (IllegalArgumentException exception) { System.err.println (ID + NL + "Unable to locate the Configuration file " + configuration_filename + "." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } // Establish the Database connection. Database database = null; try { database = new Database (configuration); database.Connect (database_server); } catch (Database_Exception exception) { System.err.println (ID + NL + "Unable to connect to the database." + NL + exception.getMessage ()); System.exit (EXIT_DATABASE_ERROR); } catch (Configuration_Exception exception) { System.err.println (ID + NL + "Unable to connect to the database." + NL + exception.getMessage ()); System.exit (EXIT_CONFIGURATION_PROBLEM); } if ((DEBUG & DEBUG_MAIN) != 0) System.out.println (" The_Database Data_Port Configuration -" + NL + database.Data_Port ().Configuration ().Description ()); // Construct the Update_DB object. if (verbose) System.out.println (ID); update_DB = new Update_DB (database); } // Set the modes. update_DB .Verbose (verbose) .Update_One_Only (update_one_only) .Ignore_Multiple_Updates (ignore_multiple_updates) .Delete (delete); // Apply the table and fields set. try { records += update_DB.Update_Database (table_reference, field_assignments); } catch (IllegalArgumentException exception) { System.err.println (exception.getMessage ()); Disconnect (update_DB); System.exit (EXIT_ILLEGAL_ARGUMENT); } catch (Database_Exception exception) { System.err.println (exception.getMessage ()); Disconnect (update_DB); System.exit (EXIT_DATABASE_ERROR); } } Disconnect (update_DB); if (records > EXIT_STATUS_LIMIT) records = EXIT_STATUS_LIMIT; System.exit (records); } /** Disconnects the Database of an Update_DB.

If an exception is thrown by the Database Disconnect the exception message is reported to stderr.

@param update_DD An Update_DB object. This may be null in which case nothing is done. */ private static void Disconnect ( Update_DB update_DB ) { if (update_DB == null) return; try { update_DB.Database ().Disconnect (); } catch (Database_Exception exception) { System.err.println (ID + NL + "Unable to disconnect from the database." + NL + exception.getMessage ()); } } /** Command line usage syntax.

Usage: Update_DB <Switches>
  Switches -
    [-Configuration [<filename>]]
    [-Database|-Server <server name>]
    -Table|-Reference [<catalog>.]<table>[:<key>]
    <field_name>=<value> [...]
    [-1[+]]
    [-+]
    [-[No_]DElete>]
    [-Verbose]
    [-Quiet]
    [-Help]

Configuration:

If the name of the {@link PIRL.Configuration.Configuration Configuration} file is not specified the {@link #DEFAULT_CONFIGURATION_FILENAME} will be used. If the configuration file is not in the current working directory, it will be looked for in the user's home directory. The configuration file must contain the necessary information needed to identify and connect with the database server (as required by the {@link PIRL.Database.Database#Database(Configuration) Database} constructor and its {@link PIRL.Database.Database#Connect() Connect} method). These are typically the server "Host" name and database "Type", "User" and "Password" access values.

Only one configuration file may be used.

Database server name:

The Configuration file may contain connection information for more than one database. The information for each database is organized by {@link Database#SERVER Server} name, which may be specified. If no server name is provided, the Database will attempt to use the default (first) Server specified in the Configuration.

Only one database server may be used.

Table reference:

A Table Reference specifies a <table>, and its containing <catalog>, to be updated. If the catalog is not specified the Database will attempt to use the configuration Catalog parameter for the Server group, or a default Catalog parameter. The optional <key> is an SQL conditional expression that is used to select the record(s) to be updated. N.B.: If the key specification contains spaces then it must be quoted; if it contains quotes then these must be escaped from shell interpretation (by a preceeding backslash) or the the specification is already quoted then the specication quotes must be different from the enclosing quotes (use double quotes to enclose the specification and single quotes in the specification content). If the key is not present, a new record will be inserted into the table.

Field value assignments:

The list of one or more <field_name>=<value> pairs specifies the name of the record fields and the values they are to receive. N.B.: There are no spaces around the '=' character, unless the entire assignment is quoted; if the value contains spaces then it must be quoted and quotes in the value specification must be escaped from the shell or be different from any enclosing quotes. If an update is occuring, only the specified fields will be modified. If a record is being inserted, any records of the field that are not specified will automatically be given their default values by the database server.

One (1) record update:

By default, more than one record may be updated for each table reference and field assignment list. By specifying the -1 option, only one record will be allowed to be updated for each set. In this case, if more than one record would be updated as a result of the key selection this will be prevented and processing will abort. However, if '+' is appended to this option processing will not abort though the update will not be done.

The -+ option will enable multiple record updates. This is useful when multiple update sets are specified and a previous set disabled multiple record updates but the following sets should allow it.

Deletion:

Instead of updating records in the table selected records may be deleted from the table. By default, or if -no_delete is specified, table records are updated. A record selection key is required when delete mode is in effect, and any field value assignments will be ignored. One record update checking applies to record deletion.

Update sets:

A table reference and its following field value assignments, plus any preceeding mode switches, up to the next table reference, if any, constitutes an update set. Multiple update sets may be specified on a command line. Each update set will be processed before any following command line arguments are processed. The configuration file and modes in effect at that point will apply to the update set processing. An error encountered in an update set halts all further processing of the command line.

Arguments files:

At any point on the command line an argument of the form "@<filename>" may be specified. The file specified will be read for command line arguments that are processed in the order they occur in the file. When the end of file has been reached processing continues with the next argument on the command line. In effect, the content of an argument file replaces the @<filename> argument on the command line. If the dash character ('-') is used for the <filename> the arguments will be read from the standard input. Any argument in an argument file may be an argument file argument. Circular references are detected and cause an error exit.

Verbose/Quiet:

The verbose option provides a log of operations as they occur printed to the standard output. The quiet option disables the verbose mode. By default quiet mode is in effect.

Help:

The command line syntax usage is listed and the program exits. N.B.: If the -Help option occurs anywhere on the command line no other command line arguments will be used.

N.B.The usage is printed to stderr. This method always results in a System.exit with a status of {@link #EXIT_INVALID_COMMAND_LINE_SYNTAX EXIT_INVALID_COMMAND_LINE_SYNTAX}.

@param verbose If true the usage is printed, otherwise silence. */ public static void Usage ( boolean verbose ) { if (verbose) System.err.println ("Usage: Update_DB " + NL +" Switches -" + NL +" [-Configuration []]" + " (default: " + DEFAULT_CONFIGURATION_FILENAME + ")" + NL +" [-Database|-Server ]" + " (default: first configuration Server)" + NL +" -Table | -Reference [.][:]" + NL +" = [...]" + NL +" [-1[+]]" + " (default: disabled, multiple record updates OK)" + NL +" [-+]" + " (default: enabled, multiple record updates OK)" + NL +" [-[No_]DElete]" + " (default: no deletion)" + NL +" [-Verbose]" + " (default: false)" + NL +" [-Quiet]" + " (default: true)" + NL +" [-Help]" + NL ); System.exit (EXIT_INVALID_COMMAND_LINE_SYNTAX); } } pirl-2.3.8/PIRL/Database/SQL_Listener.java0000644000175000017500000000275011742733571020003 0ustar mathieumathieu/* SQL_Listener PIRL CVS ID: SQL_Listener.java,v 1.3 2012/04/16 06:08:57 castalia Exp Copyright (C) 2008-2012 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; /** The SQL_Listener interface is used by a Data_Port to provide notifications of SQL statements being sent to the database server.

@author Bradford Castalia, UA/PIRL @version 1.3 */ public interface SQL_Listener { /** Called by a Data_Port before an SQL statement is sent to the database server.

@param SQL The SQL statement String that will be sent to the database server. */ public void SQL_Statement ( String SQL ); } pirl-2.3.8/PIRL/Database/JDBC_Data_Port.java0000644000175000017500000024067211742733571020145 0ustar mathieumathieu/* JDBC_Data_Port PIRL CVS ID: JDBC_Data_Port.java,v 2.7 2012/04/16 06:08:57 castalia Exp Copyright (C) 2001-2008 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Configuration.Configuration; import java.sql.Connection; import java.sql.DriverManager; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.SQLException; import java.util.Vector; import java.util.Iterator; import java.util.ListIterator; import java.lang.ClassNotFoundException; /** A JDBC_Data_Port manages access to any JDBC database.

A JDBC_Data_Port provides a partial implementation of the Data_Port interface. Only the {@link #Open(Configuration) Open} method is left to be implemented by the subclass that finalizes the Data_Port implementation for the specific type of JDBC database.

@author Bradford Castalia, UA/PIRL @version 2.7 @see Data_Port @see Configuration */ public abstract class JDBC_Data_Port implements Data_Port { /* The ID and Port_Type variables should be set by the subclass that finalizes this abstract class. It's constructor should call the protected ID_Type method with its ID and Port_Type variables. */ private String ID = "PIRL.Database.JDBC_Data_Port (2.7 2012/04/16 06:08:57)", Port_Type = null; // The Database Configuration containing database server access information. private Configuration The_Configuration = null; // The JDBC Conncetion to the database server. private Connection JDBC_Connection = null; // SQL_Listener objects. private Vector SQL_Listeners = new Vector (); /** Delimiter between catalog, table and field components when more than one component is specified in a reference.

The component delimiter is initialzed when the Data_Port is {@link #Open_Data_Port(String) open}ed by the server specific subclass using the getCatalogSeparator value obtained from the database metadata.

The default is ".". */ protected String Component_Delimiter = "."; /** Determines if database entity identifiers are case sensitive.

A database server that does not use case sensitive entity identifiers will coerce the identifiers to lowercase. When these identifiers are returned from a query they will not match user specified identifiers that are in mixed case.

Because some database systems on some operating systems are not case sensitive in handling entity identifiers (the names of catalogs, tables, and field names) it may be necessary to enforce case insensitivity when matching user specified names against identifiers returned from the database server.

The case sensitivity of identifiers is initialzed when the Data_Port is {@link #Open_Data_Port(String) open}ed by the server specific subclass by using the {@link DatabaseMetaData#supportsMixedCaseIdentifiers()} value obtained from the database metadata.

The default, until the Data_Port has set the value, is to assume that database identifiers are not case sensitive.

@see #Case_Sensitive_Identifiers() @see Database#Matches(String, String) */ protected boolean Case_Sensitive_Identifiers = false; /** Determines if the database server schema structure is to be treated as the Database class "catalog" structure.

The meaning of the term "catalog" to the Database class is the database server structure that contains a collection of data tables. For some database servers the term "database" is used for this structure (e.g. MySQL), but the same JDBC catalog structure applies. However, for some database servers the corresponding structure is a "schema" (e.g. PostgreSQL) and the JDBC schema structure must be used to access it rather than using its catalog structure.

This flag is expected to be set by database specific subclasses that complete the Data_Port implementation. It will be used by methods that need to make the distinction when using a JDBC method. */ protected boolean Treat_Schema_As_Catalog = false; private String Database_Catalog_Term = "catalog", Database_Schema_Term = "schema"; // New-Line sequence. private static final String NL = Database.NL; // DEBUG control. private static final int DEBUG_OFF = 0, DEBUG_ALL = -1, DEBUG_CONFIGURE = 1 << 0, DEBUG_OPEN = 1 << 1, DEBUG_DESCRIPTION = 1 << 2, DEBUG_QUERY = 1 << 3, DEBUG_UPDATE = 1 << 4, DEBUG_UTILITY = 1 << 5, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs an unopened JDBC_Data_Port. */ public JDBC_Data_Port () {} /*============================================================================== Accessors */ /** Tests if the Data_Port currently has an active Connection.

@return true if the Data_Port has access to a database; false otherwise. */ public boolean is_Open () { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (">-< JDBC_Data_Port.is_Open: " + (JDBC_Connection != null)); // Data_Port Implementation return JDBC_Connection != null; } /** Gets the JDBC Connection object for the Data_Port.

@return The JDBC Connection object for the Data_Port. This will be null if the Data_Port does not have an active Connection. */ public Connection Connection () { // Data_Port Implementation return JDBC_Connection; } /** Adds a SQL_Listener to the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be added. If null, or the listener is already listed, nothing is done. @return This Data_Port. @see SQL_Listener */ public Data_Port Add_SQL_Listener ( SQL_Listener listener ) { // Data_Port Implementation synchronized (SQL_Listeners) { if (listener != null && ! SQL_Listeners.contains (listener)) SQL_Listeners.add (listener); } return this; } /** Removes a SQL_Listener from the list of listeners to be notified of SQL statements before they are sent to the database server.

@param listener A SQL_Listener to be removed. @return true if the listener was removed; false if the listener was not listed. @see SQL_Listener */ public boolean Remove_SQL_Listener ( SQL_Listener listener ) { // Data_Port Implementation synchronized (SQL_Listeners) {return SQL_Listeners.remove (listener);} } private void Report_SQL_Statement ( String SQL ) { // Data_Port Implementation synchronized (SQL_Listeners) { Iterator listeners = SQL_Listeners.iterator (); while (listeners.hasNext ()) ((SQL_Listener)listeners.next ()).SQL_Statement (SQL); } } /** Used by the finalizer subclass to register its class identification and specific type name.

@param ID The class identification string. This is recommended to be of the form "Package_name.Class_name (Version Date)". @param Type The name of the specific type of Data_Port. This is expected to be the value of the {@link Database#TYPE TYPE} parameter from the Configuration. */ protected void ID_Type ( String ID, String Type ) { if (ID != null) this.ID += NL + ID; if (Type == null) Port_Type = ""; else Port_Type = Type; } /*============================================================================== Methods */ /** Gets the Configuration of the Data_Port.

@return The current Configuration of the Data_Port. @see Configuration */ public Configuration Configuration () {return The_Configuration;} /*------------------------------------------------------------------------------ Access */ /** Opens a connection to a database.

This is a Data_Port interface method that is left to subclasses to implement. Note: This is the only abstract method.

Subclasses are expected to implement the Open method as follows:

  1. Update the supplied configuration with any local parameters.

    This is typically done by using its {@link Configuration#Set_Conditionally(String[][]) Set_Conditionally (Parameters[][])} methods where Parameters[][] is an array of parameter name, value string pairs. There will a set of Required Parameters and a set of Optional Parameters. Note: The {@link Database#DRIVER DRIVER} parameter is required. Its value is the classname for the JDBC driver used with the specific type of Data_Port being implemented.

  2. Call the {@link #Configure(Configuration) Configure} method with the configuration.

    This method will check that the Data_Port is not already open, confirm the presence of required parameters, and load the driver for the Data_Port.

  3. Assemble the URL string used by the JDBC driver.

    This is driver specific and generally uses parameters from the configuration. The best way to get configuration parameter values is to use the protected {@link #Config_Value(String) Config_Value} method.

  4. Call the {@link #Open_Data_Port(String) Open_Data_Port} method with the URL.

    This will invoke the JDBC DriverManager to make the connection.

@param configuration The Data_Port Configuration. @throws Database_Exception If the Data_Port could not be opened. */ public abstract void Open ( Configuration configuration ) throws Database_Exception; /** Used by the finalizer subclass to register its Configuration and load its database driver.

The Configuration must contain a {@link Database#DRIVER DRIVER} parameter. The value of this parameter is the class name of the driver. An instance of this class will be loaded for use.

@param configuration The Configuration of the specific Data_Port. This is expected to be the Configuration passed to it via its {@link #Open(Configuration) Open} method, modified as appropriate. @throws Database_Exception If the Data_Port is already open, the Configuration does not contain the necessary DRIVER parameter, or the driver could not be loaded. */ protected void Configure ( Configuration configuration ) throws Database_Exception { if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (">>> JDBC_Data_Port.Configure:"); if (is_Open ()) throw new Database_Exception ( ID + NL +"The Data Port is already open." ); // Setup the configuration. if (configuration == null) throw new Database_Exception ( ID + NL +"An invalid (null) configuration was specified." ); The_Configuration = configuration; // Load the driver. String driver_name = The_Configuration.Get_Linked_One (Database.DRIVER); if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println (" Driver: " + driver_name); if (driver_name == null) throw new Database_Exception ( ID + NL +"A \"" + Database.DRIVER + "\" parameter is required." ); try { /* Theorectically a new instance of the driver class is unnecessary. However, the documentation for the mm.mysql.jdbc driver notes that this should be done as a workaround for some "broken" Java implementations. Class.forName (driver_name).newInstance (); */ Class.forName (driver_name); } catch (Exception exception) { throw new Database_Exception ( ID + NL +"Unable to " + ((exception instanceof ClassNotFoundException) ? "find" : "load") + " the JDBC driver: " + driver_name, exception ); } if ((DEBUG & DEBUG_CONFIGURE) != 0) System.out.println ("<<< JDBC_Data_Port.Configure:"); } /** Used by the finalizer subclass to get a JDBC Connection to the database as specified by its URL.

@param URL The URL provided to the {@link DriverManager#getConnection (String) DriverManager.getConnection} method. The syntax of the URL is dependent on the database driver being used. It is likely to contain specifications obtained from the Data_Port Configuration. @throws Database_Exception If the JDBC Connection failed. */ protected void Open_Data_Port ( String URL ) throws Database_Exception { if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (">>> JDBC_Data_Port.Open_Data_Port: " + URL); // Open the data port connection. try {JDBC_Connection = DriverManager.getConnection (URL);} catch (SQLException exception) { // Mask passwords in the SQL exception messages. throw new Database_Exception ( Description () + NL +"Connection failed to: " + URL, Database_Exception.masked_SQLException (exception) ); } try { DatabaseMetaData metadata = JDBC_Connection.getMetaData (); // Initialize identifier case sensitivity. Case_Sensitive_Identifiers = metadata.supportsMixedCaseIdentifiers (); // Initialize the database server "catalog" term. Database_Catalog_Term = metadata.getCatalogTerm (); // Initialize the database server "schema" term. Database_Schema_Term = metadata.getSchemaTerm (); // Initialize the catalog and table delimiter. Component_Delimiter = metadata.getCatalogSeparator (); if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (" Case_Sensitive_Identifiers = " + Case_Sensitive_Identifiers + NL +" Database_Catalog_Term = " + Database_Catalog_Term + NL +" Database_Schema_Term = " + Database_Schema_Term + NL +" Component_Delimiter = " + Component_Delimiter); } catch (Exception exception) {/* Leave the defaults */} if ((DEBUG & DEBUG_OPEN) != 0) System.out.println ("<<< JDBC_Data_Port.Open_Data_Port"); } /** Closes the JDBC Connection.

If the Connection is already closed, nothing is done.

@throws Database_Exception If there was a problem closing the JDBC Connection. */ public void Close () throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_OPEN) != 0) System.out.println (">-< JDBC_Data_Port.Close"); if (is_Open ()) { String description = Description (); try {JDBC_Connection.close ();} catch (Exception exception) { throw new Database_Exception ( description + NL +"Problem closing data port.", exception ); } finally {JDBC_Connection = null;} } } /*------------------------------------------------------------------------------ Description */ /** Provides a String identifying the Data_Port.

The identification includes the Data_Port Type, if known, the class ID for this JDBC_Data_Port and the class ID for the finalizer subclass.

@return The identifification for the Data_Port. */ public String toString () { // Data_Port Implementation String string = ""; if (Port_Type != null && Port_Type.length () > 0) string += Port_Type + ' '; string += "Data Port:" + NL + ID; return string; } /** Provides a multi-line description of the Data_Port.

If the Data_Port is currently {@link #Open Open}, a description of the database connection may be included.

@return The multi-line description of the Data_Port and any active database connection. */ public String Description () { // Data_Port Implementation String description = toString (); String string = Config_Value (Configuration.USER); if (string.length () == 0) string = "(none)"; description += NL + " User: " + string; string = Config_Value (Configuration.HOST); if (string.length () == 0) string = "(none)"; description += NL + " Host: " + string; if (is_Open ()) { try { DatabaseMetaData metadata = JDBC_Connection.getMetaData (); description += NL + " Driver: " + metadata.getDriverName () + " (" + Config_Value (Database.DRIVER) + ")" + NL + " version " + metadata.getDriverVersion () + NL + " Server: " + metadata.getDatabaseProductName () + " version " + metadata.getDatabaseProductVersion (); } catch (Exception exception) {} } else description += NL + "Not connected to a database."; return description; } /** Gets a String describing the contents of selected catalogs and tables.

For each table being described each field name and its data type is listed.

@param catalog The catalog to have its contents described. If it is in {@link #Table_Reference(String, String) table reference} format, the catalog portion of the name will be used. If null, all catalogs on the database server will be described. @param table The table to have its contents described. If it is in {@link #Table_Reference(String, String) table reference} format, the table portion of the name will be used. If null, all tables in the catalog (or all catalogs) will be described. @return A descriptive String. */ public String Contents ( String catalog, String table ) { // Data_Port Implementation String description = ""; if (is_Open ()) { try { DatabaseMetaData metadata = JDBC_Connection.getMetaData (); Vector catalog_collection = Catalogs (), catalogs, table_collection, tables = null, names, types, sizes; if (catalog == null) catalogs = catalog_collection; else { String name = Catalog_Name (catalog); if (name.length () == 0) // Take the argument String as-is; name = catalog; if (! Database.Matches (catalog_collection, name, Case_Sensitive_Identifiers)) return "Unable to obtain the Contents of catalog \"" + name + "\"." + NL + "It doesn't exist or access is denied."; catalogs = new Vector (1); catalogs.add (name); } if (table != null) { tables = new Vector (1); tables.add (table_name ("get Contents", table)); } description += "Identifiers are " + (Case_Sensitive_Identifiers ? "" : "not ") + "case sensitive." + NL; if (Treat_Schema_As_Catalog) description += "Database server schema are treated as Database catalogs." + NL; description += NL; // Catalog descriptions: Iterator catalog_list = catalogs.iterator (); while (catalog_list.hasNext ()) { catalog = (String)catalog_list.next (); table_collection = Tables (catalog); if (table == null) tables = table_collection; else if (! Database.Matches (table_collection, table, Case_Sensitive_Identifiers)) continue; description += catalog + " " + Database_Catalog_Term + ":" + NL; // Table descriptions: Iterator table_list = tables.iterator (); while (table_list.hasNext ()) { String catalog_table = (String)table_list.next (); description += " " + catalog_table + " table" + NL; catalog_table = catalog + Component_Delimiter + catalog_table; // Field descriptions: names = Field_Names (catalog_table); types = Field_Types (catalog_table); description += " Fields" + NL + " ------" + NL; for (int field = 0; field < names.size (); field++) description += " " + field + " - " + names.elementAt (field) + ": " + types.elementAt (field) + NL; } } } catch (Exception exception) { description += Description () + NL + "Contents could not be completed." + NL + Database_Exception.exception_String (exception); } } else description = "Not connected to a database."; return description; } /** Gets the list of accessible catalogs in the database.

@return A Vector of Strings naming all the catalogs in the database. @throws Database_Exception If the Data_Port is not open, or the database rejects the operation. */ public Vector Catalogs () throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (">>> JDBC_Data_Port.Catalogs"); if (is_Open ()) { try { // Get the catalog list from the database metadata. String results_column_name, column_name; ResultSet catalog_set; if (Treat_Schema_As_Catalog) { results_column_name = "TABLE_SCHEM"; catalog_set = JDBC_Connection.getMetaData ().getSchemas (); } else { results_column_name = "TABLE_CAT"; catalog_set = JDBC_Connection.getMetaData ().getCatalogs (); } if ((DEBUG & DEBUG_DESCRIPTION) != 0) { System.out.println (" " + (Treat_Schema_As_Catalog ? "Schemas" : "Catalogs") + " metadata results:"); report_ResultSetMetaData (catalog_set); } column_name = column_name_for_ResultSet (results_column_name, catalog_set); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" Getting names from ResultSet column \"" + column_name + '"'); if (column_name == null) throw new Database_Exception ("The database metadata does not contain the expected \"" + results_column_name + "\" column name."); Vector catalogs = new Vector (); while (catalog_set.next ()) catalogs.add (catalog_set.getString (column_name)); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" " + catalogs.size () + " catalogs: " + catalogs + NL +"<<< JDBC_Data_Port.Catalogs"); return catalogs; } catch (Exception exception) { throw new Database_Exception ( Description () + NL + "Unable to obtain the Catalogs list.", exception ); } } else throw new Database_Exception ( Description () + NL + "Unable to obtain the Catalogs list." + NL + "Not connected to a database." ); } /** Obtains the list of accessible tables contained in a catalog.

If the catalog name is null, the {@link Database#CATALOG CATALOG} from the Data_Port Configuration will be used. The catalog name may be in catalog.table format, in which case only the catalog portion of the name will be used.

@param catalog The name of the database calolog to examine. If this is in {@link #Table_Reference(String, String) table reference} format only the catalog part will be used. If null or empty the {@link Database#CATALOG CATALOG} value from the {@link #Configuration() configuration}, if available, will be used. @return A Vector of Strings naming all the tables in the catalog. If the catalog does not exist in the database, null will be returned. An empty Vector will be returned for a catalog that does not contain any tables. @throws Database_Exception If the Data_Port is not open, no catalog name is available, or the database rejects the operation. */ public Vector Tables ( String catalog ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (">>> JDBC_Data_Port.Tables: catalog " + catalog); if (is_Open ()) { // Use the catalog name known to the database server. try {catalog = database_catalog_name ("Tables", catalog, false);} catch (Database_Exception exception) { throw new Database_Exception ("Unable to obtain Tables for catalog \"" + catalog + "\".", exception ); } if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" Getting tables for catalog " + catalog); try { // Get the tables list from the database metadata. String database_catalog = catalog, database_schema = "%"; if (Treat_Schema_As_Catalog) { /* Since schema are being treated as Database catalogs it is presumed that a connection has been made to a particular database catalog that can be identified. */ database_catalog = JDBC_Connection.getCatalog (); database_schema = catalog; } ResultSet table_set = JDBC_Connection.getMetaData ().getTables (database_catalog, database_schema, "%", null); String column_name = column_name_for_ResultSet ("TABLE_NAME", table_set); if ((DEBUG & DEBUG_DESCRIPTION) != 0) { report_ResultSetMetaData (table_set); System.out.println (" Getting names from ResultSet column \"" + column_name + '"'); } if (column_name == null) throw new Database_Exception ("The database metadata does not contain the expected " + "\"TABLE_NAME\" column name."); Vector tables = new Vector (); while (table_set.next ()) tables.add (table_set.getString (column_name)); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println ("<<< JDBC_Data_Port.Tables:" + NL +" " + tables.size () + " tables: " + tables); return tables; } catch (Exception exception) { throw new Database_Exception ( Description () + NL +"Unable to obtain Tables for catalog \"" + catalog + "\".", exception ); } } else throw new Database_Exception ( Description () + NL +"Unable to obtain Tables for catalog \"" + catalog + "\"." + NL +"Not connected to a database." ); } /** Gets the list of field names in a table on the database server.

@param table The name of the table to be examined. If null, then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field name Strings. If the table does not exist in the Database, a null will be returned. @throws Database_Exception If the Data_Port is not open, no catalog name is available, or the operation on the database server failed. */ public Vector Field_Names ( String table ) throws Database_Exception { // Data_Port Implementation return Fields (table, "COLUMN_NAME"); } /** Gets the list of field data types in a table on the database server.

@param table The name of the table to be examined. If null, then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field name Strings. If the table does not exist in the Database, a null will be returned. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public Vector Field_Types ( String table ) throws Database_Exception { // Data_Port Implementation return Fields (table, "TYPE_NAME"); } /** Obtains a list of field information for the table of a catalog.

The specified field_info must be a tag corresponding to one of the field information names obtained from the JDBC getColumns method:

TABLE_CAT (String)
Table catalog (may be null)
TABLE_SCHEM (String)
Table schema (may be null)
TABLE_NAME (String)
Table name
COLUMN_NAME (String)
Column (i.e. field) name
DATA_TYPE (short)
SQL type from java.sql.Types
TYPE_NAME (String)
Data source dependent type name, for a UDT the type name is fully qualified.
COLUMN_SIZE (int)
Column size. For char or date types this is the maximum number of characters, for numeric or decimal types this is precision.
BUFFER_LENGTH (unused)
DECIMAL_DIGITS (int)
The number of fractional digits.
NUM_PREC_RADIX (int)
Radix (typically either 10 or 2)
NULLABLE (int)
Is NULL allowed?
columnNoNulls
- might not allow NULL values
columnNullable
- definitely allows NULL values
columnNullableUnknown
- nullability unknown
REMARKS (String)
Comment describing column (may be null)
COLUMN_DEF (String)
Default value (may be null)
SQL_DATA_TYPE (unused)
SQL_DATETIME_SUB (unused)
CHAR_OCTET_LENGTH (int)
For char types the maximum number of bytes in the column.
ORDINAL_POSITION (int)
Index of the column in the table (starting at 1).
IS_NULLABLE (String)
NO
- column definitely does not allow NULL values
YES
- column might allow NULL values
An empty string
- nobody knows

@param table The name of the table to be examined. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param field_info The tag String for selecting field information. @return A Vector of field information Strings, one per field. For info that is originally numeric (as indicated in the table) a String representation is returned. @throws Database_Exception If no catalog or table name is available, or the database server rejected the operation. @see java.sql.DatabaseMetaData#getColumns(String, String, String, String) */ public Vector Fields ( String table, String field_info ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (">>> JDBC_Data_Port.Fields: table " + table + NL +" field info: " + field_info); if (is_Open ()) { // Both catalog and table names are required. String catalog = null, catalog_table = null; // Use the catalog and table names known to the database server. try { catalog = database_catalog_name ("Fields", Catalog_Name (table), false); catalog_table = database_table_name ("Fields", table, false); } catch (Database_Exception exception) { throw new Database_Exception ("Unable to obtain the Fields information for table \"" + table + "\".", exception); } try { // Get the field names from the database metadata. String database_catalog = catalog, database_schema = null; if (Treat_Schema_As_Catalog) { /* Since schema are being treated as Database catalogs it is presumed that a connection has been made to a particular database catalog that can be identified. */ database_catalog = JDBC_Connection.getCatalog (); database_schema = catalog; } if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" Getting columns for table " + catalog + Component_Delimiter + catalog_table); ResultSet fields_set = JDBC_Connection.getMetaData ().getColumns (database_catalog, database_schema, catalog_table, "%"); String column_name = column_name_for_ResultSet (field_info, fields_set); if ((DEBUG & DEBUG_DESCRIPTION) != 0) { report_ResultSetMetaData (fields_set); System.out.println (" Getting names from ResultSet column " + column_name); } if (column_name == null) throw new Database_Exception ("The database metadata does not contain the expected \"" + field_info + "\" column name."); Vector fields = new Vector (); while (fields_set.next ()) fields.add (fields_set.getString (column_name)); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println ("<<< JDBC_Data_Port.Fields:" + NL +" " + fields.size () + " fields: " + fields); return fields; } catch (Exception exception) { throw new Database_Exception ( Description () + NL +"Unable to obtain the Fields information for table \"" + catalog + Component_Delimiter + catalog+table + "\".", exception ); } } else throw new Database_Exception ( Description () + NL +"Unable to obtain the Fields information for table \"" + table + "\"." + NL +"Not connected to a database." ); } /** Gets a list of primary keys for a table.

@param table The name of the table to be examined. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @return A Vector of field name Strings. @throws Database_Exception If no catalog and table name is available, or the database server rejected the operation. */ public Vector Keys ( String table ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (">>> JDBC_Data_Port.Keys: table " + table); if (is_Open ()) { // Both catalog and table names are required. String catalog = null, catalog_table = null; // Use the catalog and table names known to the database server. try { catalog = database_catalog_name ("Keys", Catalog_Name (table), false); catalog_table = database_table_name ("Keys", table, false); } catch (Database_Exception exception) { throw new Database_Exception ("Unable to obtain the Keys for table \"" + table + "\".", exception); } try { // Get the primary keys from the database metadata. String database_catalog = catalog, database_schema = null; if (Treat_Schema_As_Catalog) { /* Since schema are being treated as Database catalogs it is presumed that a connection has been made to a particular database catalog that can be identified. */ database_catalog = JDBC_Connection.getCatalog (); database_schema = catalog; } if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" Getting primary keys for table " + catalog + Component_Delimiter + catalog_table); ResultSet keys_set = JDBC_Connection.getMetaData ().getPrimaryKeys (database_catalog, database_schema, catalog_table); String column_name = column_name_for_ResultSet ("COLUMN_NAME", keys_set); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" Getting names from ResultSet column \"" + column_name + '"'); if (column_name == null) throw new Database_Exception ("The database metadata does not contain the expected \"" + "\"COLUMN_NAME\" column name."); Vector keys = new Vector (); while (keys_set.next ()) keys.add (keys_set.getString (column_name)); if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println ("<<< JDBC_Data_Port.Keys:" + NL +" " + keys.size () + " keys: " + keys); return keys; } catch (Exception exception) { throw new Database_Exception ( Description () + NL +"Unable to obtain the Keys for table \"" + catalog + Component_Delimiter + catalog_table + "\".", exception ); } } else throw new Database_Exception ( Description () + NL +"Unable to obtain the Keys for table \"" + table + "\"." + NL +"Not connected to a database." ); } /*------------------------------------------------------------------------------ Query */ /** Submit an SQL query.

The SQL_query String is an SQL statment that requests data to be extracted (not removed) from the database. The syntax, though "standardized", is likely to vary in its particulars dependent on the database server implementation.

A query always returns a table. The table is in the form of a Vector of data record Vectors. The first record contains the names (String) of each field for the data in corresponding locations of all the field value records that follow; i.e. these are the table column names. The field values in each data record are always provided in String representation regardless of the data type stored in the database. However, NULL field values remain unchanged as they are distinct from an otherwise valid field value.

Note: A field value will either be a String or null.

N.B.: All {@link #Add_SQL_Listener(SQL_Listener) SQL statement listeners} are notified before the SQL is sent to the database server.

@param SQL_query The syntax of the query string is database dependent. A typical example is an SQL "SELECT" statement. @param limit The maximum number of records to return. If negative, there will be no limit to the number of records returned. If zero, no records will be returned. @return A data table in form of a Vector of String Vectors (records). The first record is the field names for the field values in all subsequent records. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public Vector Query ( String SQL_query, int limit ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_QUERY) != 0) System.out.println (">>> JDBC_Data_Port.Query:" + NL +" SQL - " + SQL_query); Report_SQL_Statement (SQL_query); if (is_Open ()) { try { // Submit the SQL query. Statement statement = JDBC_Connection.createStatement (); ResultSet results = statement.executeQuery (SQL_query); // Assemble the results into a Vector of String Vector records. ResultSetMetaData results_metadata = results.getMetaData (); int total_fields = results_metadata.getColumnCount (); Vector table = new Vector (), field_names = new Vector (total_fields), record; // Field names record. for (int field = 1; field <= total_fields; field++) field_names.add (results_metadata.getColumnName (field)); table.add (field_names); // Data records. while (limit-- != 0 && results.next ()) { record = new Vector (total_fields); for (int field = 1; field <= total_fields; field++) record.add (results.getString (field)); table.add (record); } statement.close (); if ((DEBUG & DEBUG_QUERY) != 0) System.out.println ("<<< JDBC_Data_Port.Query"); return table; } catch (Exception exception) { throw new Database_Exception ( Description () + NL +"Query failed for -" + NL + SQL_query, exception ); } } else throw new Database_Exception ( Description () + NL +"Query failed for -" + NL + SQL_query + NL +"Not connected to a database." ); } /** Selects from one or more tables the data values from one or more named fields from the data records that satisfy the selection conditions.

Note: Each table name that is not in the format catalog.table will have the {@link Database#CATALOG CATALOG} from the Configuration prepended.

N.B.: If the limit argument is zero the SQL assmebled for the database query will be returned instead of submitting the query.

@param tables The Vector of database tables from which to select records. If this is null, the tables (one or more) specified by the {@link Database#TABLE TABLE} parameter will be used. @param fields A Vector of field name Strings specifying the data fields (columns) to be extracted. If this is null or empty, all data fields will be extracted. @param conditions A String in SQL WHERE clause syntax (without the keyword "WHERE") specifying the conditions for selection of a data record from the database. The specific syntax of the conditions string is database dependent. If this is null, no conditions will be applied; all data records will be used. @param limit The maximum number of records to return. If negative, there will be no limit to the number of records returned. N.B.: If zero, the returned vector will contain a single string that is the SQL statement that would have been submitted to the Query method. @return A Vector of String Vectors data table,as provided by the {@link #Query(String, int) Query} method. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public Vector Select ( Vector tables, Vector fields, String conditions, int limit ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_QUERY) != 0) System.out.println (">-< JDBC_Data_Port.Select:" + NL +" tables: " + tables + NL +" fields: " + fields + NL +" conditions: " + conditions); String query = "SELECT ", list = List_String (fields); if (list.length () == 0) list = "*"; query += list; String catalog = Config_Value (Database.CATALOG); if (tables == null || tables.isEmpty ()) tables = The_Configuration.Get_Linked (Database.TABLE); if (tables != null && catalog.length () != 0) { // Apply the default catalog. ListIterator table_list = tables.listIterator (); while (table_list.hasNext ()) { String table = (String)table_list.next (); if (table.length () != 0 && table.indexOf (Component_Delimiter) < 0) table_list.set (catalog + Component_Delimiter + table); } } list = List_String (tables); if (list.length () == 0) throw new Database_Exception ( Description () + NL +"Unable to Select without a table specified." + NL +"This can be provided with a \"" + Database.TABLE + "\" Configuration parameter." ); query += " FROM " + list; if (conditions != null && conditions.length () != 0) query += " WHERE " + conditions; if (limit == 0) { Vector SQL_statement = new Vector (1); SQL_statement.add (query); return SQL_statement; } return Query (query, limit); } /*------------------------------------------------------------------------------ Update */ /** Submit an SQL update.

The syntax of the SQL_update String is database dependent. These operations modify the database and return a count of the number of modifications, which may be 0 in some cases (e.g. when a catalog or table is created). Typical examples are SQL "UPDATE", "INSERT", and "ALTER" statements.

N.B.: All {@link #Add_SQL_Listener(SQL_Listener) SQL statement listeners} are notified before the SQL is sent to the database server.

@param SQL_update The SQL_update statement. @return A count of the number of modifications. @throws Database_Exception If the Data_Port is not open or the operation on the database server failed. */ public int Update ( String SQL_update ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Update: SQL -" + NL + SQL_update); Report_SQL_Statement (SQL_update); if (is_Open ()) { try { Statement statement = JDBC_Connection.createStatement (); int count = statement.executeUpdate (SQL_update); statement.close (); if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Update: " + count); return count; } catch (Exception exception) { throw new Database_Exception ( Description () + NL +"Update failed for -" + NL + SQL_update, exception ); } } else throw new Database_Exception ( Description () + NL +"Update failed for -" + NL + SQL_update + NL +"Not connected to a database." ); } /*.............................................................................. Catalogs: */ /** Creates a catalog in the database.

@param catalog The name of the catalog to create. If it is in {@link #Table_Reference(String, String) table reference} format, the catalog part will be used. A null catalog name, or a name of a catalog already in the database, is ignored. @throws Database_Exception If the database rejected the operation. */ public void Create ( String catalog ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Create: catalog " + catalog); String name = Catalog_Name (catalog); if (name.length () > 0) catalog = name; try { if (catalog != null && ! Database.Matches (Catalogs (), catalog, Case_Sensitive_Identifiers)) Update ("CREATE " + (Treat_Schema_As_Catalog ? Database_Schema_Term : Database_Catalog_Term) + " " + catalog); } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Create catalog \"" + catalog + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Create"); } /** Deletes a catalog from the database.

@param catalog The name of the catalog to delete. A null catalog name, or a name that is not a catalog in the database, is ignored. @throws Database_Exception If the database rejected the operation. */ public void Delete ( String catalog ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Delete: catalog " + catalog); String name = Catalog_Name (catalog); if (name.length () > 0) catalog = name; try { if (catalog != null && Database.Matches (Catalogs (), catalog, Case_Sensitive_Identifiers)) Update ("DROP " + (Treat_Schema_As_Catalog ? Database_Schema_Term : Database_Catalog_Term) + " " + catalog); } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Delete catalog \"" + catalog + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Delete"); } /*.............................................................................. Tables: */ /** Creates data tables with their fields and the data types of the fields.

If the table does not exist, a new one is created. For each name String in the fields Vector, if the field does not exist, it is created with the data type from the corresponding entry in the types Vector. If the field is already present in the table, its data type is changed if needed.

A table name that is not in the format catalog.table will have the {@link Database#CATALOG CATALOG} from the Configuration prepended. If the catalog does not exist in the database, it is created.

@param table The name of the table to be affected. If null, the {@link Database#TABLE TABLE} from the Configuration, if any, will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be used. If null, nothing is done. @param types The Vector of data type names to be applied to the corresponding fields. If null, nothing is done. @throws Database_Exception If the Data_Port is not open, the number of fields and types are not the same, no table name is available, or the operation on the database server failed. */ public void Create ( String table, Vector fields, Vector types ) throws Database_Exception { // Data_Port Implementation if (fields == null || fields.isEmpty () || types == null || types.isEmpty ()) // Ignored: nothing to do. return; if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Create: table " + table + NL +" fields: " + fields + NL +" types: " + types); if (fields.size () != types.size ()) throw new Database_Exception ( Description () + NL +"Unable to Create table \"" + table + "\"." + NL +"There " + ((fields.size () == 1) ? "is" : "are") + fields.size () + " field" + ((fields.size () == 1) ? " " : "s ") + "but " + types.size () + " type" + ((types.size () == 1) ? "." : "s.") ); // Both a catalog and table name are required. String catalog = catalog_name ("Create", table); table = table_name ("Create", table); Vector tables; try {tables = Tables (catalog);} catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Create table \"" + table + "\".", exception ); } if (Database.Matches (tables, table, Case_Sensitive_Identifiers)) { // Alter an existing table. table = catalog + Component_Delimiter + table; String alter = "ALTER TABLE " + table; try { Vector field_names = Field_Names (table), field_types = Field_Types (table); Iterator field_list = fields.iterator (), type_list = types.iterator (); while (field_list.hasNext ()) { String field = (String)field_list.next (), type = (String)type_list.next (); int index = Database.Index (field_names, field, Case_Sensitive_Identifiers); if (index >= 0) { // Existing field. if (! type.equalsIgnoreCase ((String)field_types.get (index))) // Change the field type to that specified. Update (alter + " MODIFY " + field + " " + type); } else // Add a new field. Update (alter + " ADD " + field + " " + type); } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Create (alter) table \"" + table + "\".", exception ); } } else { // Create a new table. if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (" CREATE TABLE"); try { // Ensure that the catalog exists. Create (catalog); String create = "CREATE TABLE " + catalog + Component_Delimiter + table + " ("; Iterator field_list = fields.iterator (), type_list = types.iterator (); String delimiter = null; while (field_list.hasNext ()) { if (delimiter == null) delimiter = ", "; else create += delimiter; create += (String)field_list.next () + " " + (String)type_list.next (); } Update (create + ")"); } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Create table \"" + table + "\".", exception ); } } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Create"); } /** Deletes fields from a data table, or the entire table.

Each field in the fields list is deleted from the table. If the field is not present in the table, it is ignored. If the fields list is null, the entire table is deleted from the catalog. Note: A table is not deleted even if all of its fields are deleted. If the table does not exist in the database, nothing is done.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be used. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public void Delete ( String table, Vector fields ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Delete: table " + table + NL +" fields: " + fields); // Both a catalog and table name are required. String catalog = catalog_name ("Delete", table); table = table_name ("Delete", table); Vector tables; try {tables = Tables (catalog);} catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Delete table \"" + table + "\".", exception ); } if (Database.Matches (tables, table, Case_Sensitive_Identifiers)) { table = catalog + Component_Delimiter + table; if (fields == null) { try {Update ("DROP TABLE " + table);} catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Delete table \"" + table + "\".", exception ); } } else { String delete = "ALTER TABLE " + table + " DROP "; try { Vector field_names = Field_Names (table); Iterator field_list = fields.iterator (); while (field_list.hasNext ()) { String field = (String)field_list.next (); if (Database.Matches (field_names, field, Case_Sensitive_Identifiers)) Update (delete + field); } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Delete a field from table \"" + table + "\".", exception ); } } } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Delete"); } /** Renames a table.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param name The new name for the table. If this is null, or matches the table name, nothing is done. @throws Database_Exception If no catalog or table name is available, or the database server rejected the operation. */ public void Rename ( String table, String name ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Rename: " + table + " to " + name); if (name != null && ! Database.Matches (table, name, Case_Sensitive_Identifiers)) { // Both catalog and table names are required. String catalog = catalog_name ("Rename table", table); table = table_name ("Rename table", table); try { if (Database.Matches (Tables (catalog), table, Case_Sensitive_Identifiers)) Update ("ALTER TABLE " + catalog + Component_Delimiter + table + " RENAME " + catalog + Component_Delimiter + name); } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Rename table \"" + table + "\" to \"" + name + "\".", exception ); } } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Rename"); } /** Renames fields in a table.

Each field name in the fields Vector that exists in the table is renamed to the corresponding name in the names Vector. Field names that do not exist in the table are ignored.

N.B.: The existing type for the field, obtained from the {@link #Field_Types(String) Field_Types} method, is re-applied to the field (due to requirements of the SQL syntax). This could alter other characteristics of the field.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names to be renamed. If null, nothing is done. @param names The Vector of new field names. If null, nothing is done. @throws Database_Exception If the Data_Port is not open, the number of fields and types are not the same, no table name is available, or the operation on the database server failed. */ public void Rename ( String table, Vector fields, Vector names ) throws Database_Exception { // Data_Port Implementation if (fields == null || fields.isEmpty () || names == null || names.isEmpty ()) // Ignored: nothing to do. return; if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Rename: fields for table " + table + NL +" from: " + fields + NL +" to: " + names); if (fields.size () != names.size ()) throw new Database_Exception ( Description () + NL +"Unable to Rename the fields of table \"" + table + "\"." + NL +"There " + ((fields.size () == 1) ? "is" : "are") + fields.size () + " field" + ((fields.size () == 1) ? " " : "s ") + "but " + names.size () + " name" + ((names.size () == 1) ? "." : "s.") ); // Both catalog and table names are required. table = composite_name ("Rename fields", table); try { Vector field_names = Field_Names (table), field_types = Field_Types (table); if (field_types != null && field_types.size () != 0) { Iterator field_list = fields.iterator (), name_list = names.iterator (); while (field_list.hasNext ()) { String field = (String)field_list.next (), name = (String)name_list.next (); int index = Database.Index (field_names, field, Case_Sensitive_Identifiers); if (index >= 0) Update ("ALTER TABLE " + table + " CHANGE " + field + " " + name + " " + (String)field_types.get (index)); } } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Rename fields of table \"" + table + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Rename (fields)"); } /*.............................................................................. Fields: */ /** Inserts values for selected fields into a table.

A new data record is created in the table. The fields Vector lists the names of the fields to be assigned a data value from the corresponding entry in the values Vector. The Vector of data values does not necessarily have to contain Strings, as long as each object's toString method produces a valid representation of the field value (or the object is null). Though the fields and values Vectors must be the same size, not all fields in the table need to be included; the database is expected to provide a default value for missing fields.

@param table The name of the table to be affected. If null, the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names for the data values. If null, nothing is done. @param values The Vector of data values corresponding the Vector of field names. If null, nothing is done. @return The number of records inserted; 1 if successful; 0 otherwise. @throws Database_Exception If the Data_Port is not open, the number of fields and values are not the same, no table name is available, a specified field is not in the table, or the operation on the database server failed. */ public int Insert ( String table, Vector fields, Vector values ) throws Database_Exception { // Data_Port Implementation if (fields == null || fields.isEmpty () || values == null || values.isEmpty ()) // Ignored: nothing to do. return 0; if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Insert: into table " + table + NL +" fields: " + fields + NL +" values: " + values); if (fields.size () != values.size ()) throw new Database_Exception ( Description () + NL +"Unable to Insert into table \"" + table + "\"." + NL +"There " + ((fields.size () == 1) ? "is" : "are") + fields.size () + " field" + ((fields.size () == 1) ? " " : "s ") + "but " + values.size () + " value" + ((values.size () == 1) ? "." : "s.") ); // Both catalog and table names are required. table = composite_name ("Insert", table); int count = 0; try { Vector field_names = Field_Names (table), field_types = Field_Types (table); if (field_types != null && field_types.size () != 0) { String insert = "INSERT INTO " + table + " (" + List_String (fields) + ") VALUES (", delimiter = null; Iterator field_list = fields.iterator (), value_list = values.iterator (); while (field_list.hasNext ()) { String field = (String)field_list.next (); int index = Database.Index (field_names, field, Case_Sensitive_Identifiers); if (index < 0) throw new Database_Exception ( Description () + NL +"No \"" + field + "\" field in the table." ); if (delimiter == null) delimiter = ", "; else insert += delimiter; Object value = value_list.next (); insert += Database.Value_Syntax (((value == null) ? (String)null : value.toString ()), (String)field_types.get (index)); } count = Update (insert + ')'); } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Insert into table \"" + table + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Insert: " + count); return count; } /** Updates values for selected fields into a table.

The fields Vector lists the names of the fields to be assigned a new data value from the corresponding entry in the values Vector. The Vector of data values does not necessarily have to contain Strings, as long as each object's toString method produces a valid representation of the field value (or the object is null). The records to have their field values updated will be selected by the conditions expression. This is an SQL WHERE expression (without the "WHERE" keyword). If no conditions are provided, then all records will be updated.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param fields The Vector of field names for the data values. @param values The Vector of data values corresponding the Vector of field names. @return The number of records that were modified. @throws Database_Exception If the Data_Port is not open, the number of fields and values are not the same, no table name is available, or the operation on the database server failed. */ public int Update ( String table, Vector fields, Vector values, String conditions ) throws Database_Exception { // Data_Port Implementation if (fields == null || fields.isEmpty () || values == null || values.isEmpty ()) // Ignored: nothing to do. return 0; if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Update: table " + table + NL +" fields: " + fields + NL +" values: " + values + NL +" conditions: " + conditions); if (fields.size () != values.size ()) throw new Database_Exception ( Description () + NL +"Unable to Update table \"" + table + "\"." + NL +"There " + ((fields.size () == 1) ? "is" : "are") + fields.size () + " field" + ((fields.size () == 1) ? " " : "s ") + "but " + values.size () + " value" + ((values.size () == 1) ? "." : "s.") ); // Both catalog and table names are required. table = composite_name ("Update", table); int count = 0; try { Vector field_names = Field_Names (table), field_types = Field_Types (table); if (field_types != null && field_types.size () != 0) { String update = "UPDATE " + table + " SET ", delimiter = null; Iterator field_list = fields.iterator (), value_list = values.iterator (); while (field_list.hasNext ()) { String field = (String)field_list.next (); int index = Database.Index (field_names, field, Case_Sensitive_Identifiers); if (index < 0) throw new Database_Exception ( Description () + NL +"No \"" + field + "\" field to Update." ); if (delimiter == null) delimiter = ", "; else update += delimiter; Object value = value_list.next (); update += field + "=" + Database.Value_Syntax (((value == null) ? (String)null : value.toString ()), (String)field_types.get (index)); } if (conditions != null && conditions.length () != 0) update += " WHERE " + conditions; count = Update (update); } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Update table \"" + table + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Update: " + count); return count; } /** Deletes selected records from a table.

The records to be deleted will be selected by the conditions expression. This is an SQL WHERE expression (without the "WHERE" keyword).

Warning: If no conditions are provided, then all records will be deleted leaving an empty table.

@param table The name of the table to be affected. If this is null then the {@link Database#TABLE TABLE} from the Configuration will be used. If it is not in {@link #Table_Reference(String, String) table reference} format, the {@link Database#CATALOG CATALOG} will be used. @param conditions A String in SQL WHERE clause syntax (without the keyword "WHERE") specifying the conditions for selection of a data record from the database. The specific syntax of the conditions string is database dependent. If this is null, no conditions will be applied and all data records will be deleted. @return The number of records that were deleted. @throws Database_Exception If the Data_Port is not open, no table name is available, or the operation on the database server failed. */ public int Delete ( String table, String conditions ) throws Database_Exception { // Data_Port Implementation if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println (">>> JDBC_Data_Port.Delete: from table " + table + NL +" conditions: " + conditions); // Both catalog and table names are required. String catalog = catalog_name ("Delete records", table); table = table_name ("Delete records", table); int count = 0; try { if (Database.Matches (Tables (catalog), table, Case_Sensitive_Identifiers)) { String delete = "DELETE FROM " + catalog + Component_Delimiter + table; if (conditions != null && conditions.length () != 0) delete += " WHERE " + conditions; count = Update (delete); } } catch (Database_Exception exception) { throw new Database_Exception ( "Unable to Delete records from table \"" + table + "\".", exception ); } if ((DEBUG & DEBUG_UPDATE) != 0) System.out.println ("<<< JDBC_Data_Port.Delete: " + count); return count; } /*.............................................................................. Utility: */ /** Generates a table reference from a catalog and table name.

A table reference in a Data_Port method is a composite name combining a catalog name and a table name in the format:

catalog{@link #Table_Reference_Component_Delimiter() delimiter}table

If a non-null, non-empty catalog name is specified the catalog portion of the name (which may itself be a table reference) will override any catalog name portion that might be in a table name that is already in table reference format. For null or empty table or catalog names the value of the {@link Database#CATALOG CATALOG} and/or {@link Database#TABLE TABLE} Configuration parameters will be used.

@param catalog The catalog name portion for the table reference. If it is in table reference format, the catalog portion will be used. If null or empty, the {@link Database#CATALOG CATALOG} value from the {@link #Configuration() configuration}, if available, will be used. @param table The table name portion for the table reference. If it is in table reference format, the table portion will be used. If null, the {@link Database#TABLE TABLE} value from the {@link #Configuration() configuration}, if available, will be used. @return A table reference suitable for use as a table argument of a JDBC_Data_Port method. @throws Database_Exception If a table reference can not be formed. */ public String Table_Reference ( String catalog, String table ) throws Database_Exception { String name = table; if (table == null || table.length () == 0) table = Config_Value (Database.TABLE); String table_name = Table_Name (table); if (table_name.length () > 0) { String catalog_name; if ((catalog_name = Catalog_Name (catalog)).length () > 0 || (catalog_name = Table_Name (catalog)).length () > 0 || (catalog_name = Catalog_Name (table)).length () > 0 || (catalog_name = Config_Value (Database.CATALOG)).length () > 0) return catalog_name + Component_Delimiter + table_name; } throw new Database_Exception ( Description () + NL +"Unable to form a Table_Reference using catalog \"" + catalog + "\" and table \"" + name + "\"." ); } /** Gets the catalog part of a {@link #Table_Reference(String, String) table reference}.

@param table_reference A String that may be a composite name combining a catalog name and a table name. May be null. @return The catalog name portion of the table reference. If the table_reference is null or does not contain a {@link #Component_Delimiter} character, the empty String is returned. @see #Table_Reference(String, String) */ public String Catalog_Name ( String table_reference ) { int index; if (table_reference != null && (index = table_reference.indexOf (Component_Delimiter)) >= 0) return table_reference.substring (0, index); return ""; } /** Gets the name known to the database server for a catalog name.

Only accessible catalogs can be identified. If the database server determines that the connection does not have permission to access the specified catalog then it can not be identified.

N.B.: Identifying the catalog from the list of accessible catalogs is done using the {@link #Case_Sensitive_Identifiers() case sensitivity} of the Data_Port implementation. Therefore, if case sensitive matching is used this method can only return the specified catalog name or null; in this case this method is only useful for determining if a catalog is accessible or not.

@param catalog The name of the database calolog to examine. If this is a {@link #Table_Reference(String, String) table reference} only the catalog part will be used. If null or empty the {@link Database#CATALOG CATALOG} value from the {@link #Configuration() configuration}, if available, will be used. @return The name of the catalog as it is known in the database. This will be null if a non-null, non-empty catalog name could not be identified in the database. @throws Database_Exception If the Data_Port is not open, a catalog name was not provided, or the database rejects the operation. */ public String Database_Catalog_Name ( String catalog ) throws Database_Exception { // Data_Port Implementation return database_catalog_name ("Database_Catalog_Name", catalog, true); } private String database_catalog_name ( String method, String catalog, boolean always_check ) throws Database_Exception { if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (">>> JDBC_Data_Port.database_catalog_name: " + catalog); /* Try to get a catalog name from the argument String. If that fails use the entire argument String, unless it is empty in which case try to get a default catalog name from the configuration. */ String name = Catalog_Name (catalog); if (name.length () > 0) catalog = name; else if (catalog == null || catalog.length () == 0) // Try to get a default catalog from the configuration. catalog = catalog_name (method, catalog); if (always_check || ! Case_Sensitive_Identifiers) { if (is_Open ()) { // Get the catalog list from the database metadata. Vector catalogs = null; try {catalogs = Catalogs ();} catch (Exception exception) { throw new Database_Exception ( "Unable to obtain the database catalog name for \"" + catalog + "\"." + NL + "Unable to obtain the Catalogs list.", exception ); } if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println (" " + catalogs.size () + " catalogs: " + catalogs); int index = Database.Index (catalogs, catalog, Case_Sensitive_Identifiers); if (index < 0) catalog = null; else catalog = (String)catalogs.get (index); } else throw new Database_Exception ( Description () + NL +"Unable to obtain the database catalog name for \"" + catalog + "\"." + NL +"Not connected to a database." ); } if ((DEBUG & DEBUG_DESCRIPTION) != 0) System.out.println ("<<< JDBC_Data_Port.database_catalog_name: " + catalog); return catalog; } /** Gets the table part of a {@link #Table_Reference(String, String) table reference}.

@param table_reference A String that may be a composite name combining a catalog name and a table name. May be null. @return The table name portion of the table reference. If the table_reference does not contain a {@link #Component_Delimiter} character, the entire String is returned. If it is null the empty String is returned. @see #Table_Reference(String, String) */ public String Table_Name ( String table_reference ) { if (table_reference == null) return ""; int index = table_reference.indexOf (Component_Delimiter) + 1; if (index > 0) return table_reference.substring (index); return table_reference; } /** Gets the name known to the database server for a table name.

Only accessible catalogs can be identified. If the database server determines that the connection does not have permission to access the specified catalog then it can not be identified.

N.B.: Identifying the catalog from the list of accessible catalogs is done using the {@link #Case_Sensitive_Identifiers() case sensitivity} of the Data_Port implementation. Therefore, if case sensitive matching is used this method can only return the specified catalog name or null; in this case this method is only useful for determining if a catalog is accessible or not.

@param table The name of the table to identify in the database. If null, then the {@link Database#TABLE TABLE} value from the Configuration, if present, will be used. If it is not in catalog.table format, the {@link Database#CATALOG CATALOG} value, if present, will be used. @return The name of the catalog as it is known in the database. This will be null if a catalog name could not be identified in the database. @throws Database_Exception If the Data_Port is not open, catalog and table names were not provided, or the database rejects the operation. */ public String Database_Table_Name ( String table ) throws Database_Exception { // Data_Port Implementation return database_table_name ("Database_Table_Name", table, true); } private String database_table_name ( String method, String table, boolean always_check ) throws Database_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> JDBC_Data_Port.database_table_name: " + table); String catalog_table = table_name (method, table); if (always_check || ! Case_Sensitive_Identifiers) { if (is_Open ()) { // Both catalog and table names are required. String catalog = catalog_name (method, table); try { // Get the table list from the database metadata. if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" Getting tables for catalog " + catalog); Vector tables = Tables (catalog); int index = Database.Index (tables, catalog_table, Case_Sensitive_Identifiers); if (index < 0) catalog_table = null; else catalog_table = (String)tables.get (index); } catch (Exception exception) { throw new Database_Exception ("Unable to obtain the database table name for table \"" + table + "\" in catalog \"" + catalog + "\"." + NL + "Unable to obtain the Tables list.", exception ); } } else throw new Database_Exception ( Description () + NL + "Unable to obtain the database table name for table \"" + table + "\"." + NL + "Not connected to a database." ); } if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< JDBC_Data_Port.database_table_name: " + catalog_table); return catalog_table; } /** Gets the table reference component delimiter.

@return The portion of a table reference that delimits the catalog component from the table component. @see #Table_Reference(String, String) */ public String Table_Reference_Component_Delimiter () {return Component_Delimiter;} /** @see Data_Port#Case_Sensitive_Identifiers() */ public boolean Case_Sensitive_Identifiers () {return Case_Sensitive_Identifiers;} /** @see Data_Port#Case_Sensitive_Identifiers(boolean) */ public void Case_Sensitive_Identifiers ( boolean case_sensitive ) {Case_Sensitive_Identifiers = case_sensitive;} /*============================================================================== Utility methods */ /** Gets a Value for a Configuration Parameter.

Only one Value is obtained; this will be the first Value when the Parameter is associated with an Array.

@param parameter The pathname to the Parameter. @return The String representation of the Parameter Value. If the named Parameter can not be found an empty String will be returned. */ protected String Config_Value ( String parameter ) { if (The_Configuration == null) parameter = null; else parameter = The_Configuration.Get_Linked_One (parameter); return (parameter == null) ? "" : parameter; } /** Gets a catalog name.

If the catalog_table String contains a Catalog_Name, that is returned. Otherwise the Database.CATALOG parameter from the Configuration, if present, is returned. If no catalog name can be found a Database_Exception is thrown.

@param method The name of the method that needs a catalog name. This name will be used in the Database_Exception message. @param catalog_table A String to be provided to Catalog_Name in an attempt to extract a catalog name portion. @return The name of a catalog. @throws Database_Exception If no catalog name can be found. */ protected String catalog_name ( String method, String catalog_table ) throws Database_Exception { String name = catalog_table; if ((catalog_table = Catalog_Name (catalog_table)).length () == 0 && (catalog_table = Config_Value (Database.CATALOG)).length () == 0) throw new Database_Exception ( Description () + NL + method + " requires a catalog name (given \"" + name + "\")." + NL +"This can be provided with a \"" + Database.CATALOG + "\" Configuration parameter." ); return catalog_table; } /** Gets a table name.

If the catalog_table String contains a Table_Name, that is returned. Otherwise the Database.TABLE parameter from the Configuration, if present, is returned. If no table name can be found a Database_Exception is thrown.

@param method The name of the method that needs a table name. This name will be used in the Database_Exception message. @param catalog_table A String to be provided to Table_Name in an attempt to extract a table name portion. @return The name of a table. @throws Database_Exception If no table name can be found. */ protected String table_name ( String method, String catalog_table ) throws Database_Exception { String name = catalog_table; if ((catalog_table = Table_Name (catalog_table)).length () == 0 && (catalog_table = Table_Name (Config_Value (Database.TABLE))).length () == 0) throw new Database_Exception ( Description () + NL + method + " requires a table name (given \"" + name + "\")." + NL +"This can be provided with a \"" + Database.TABLE + "\" Configuration parameter." ); return catalog_table; } /** Gets a composite name.

A composite name is the {@link #catalog_name(String, String)} plus the {@link #table_name(String, String)} separated by the {@link #Component_Delimiter}. This is effectively the same as a {@link #Table_Reference(String, String)} but derived from a single catalog_table reference name.

@param method The name of the method that needs a composite name. This name will be used in the Database_Exception message. @param catalog_table A String to be provided to catalog_name and table_name in an attempt to obtain catalog and table names. @return A table reference string with both catalog an table name parts. @throws Database_Exception If no catalog or table name can be found. */ protected String composite_name ( String method, String catalog_table ) throws Database_Exception { return catalog_name (method, catalog_table) + Component_Delimiter + table_name (method, catalog_table); } /** Produce a String representation of a Vector list.

Each list element, which must be a String of non-zero length (zero length String elements are ignored), are concatenated with the ", " delimiter between them. This produces a list representation suitable for use in SQL syntax.

@param list A Vector of Strings. @return A String containing all the non-zero length elements of the list, in the order they occur in the list, separated by the ", " delimiter. */ protected static String List_String ( Vector list ) { String list_string = ""; if (list != null && ! list.isEmpty ()) { String element, delimiter = null; Iterator elements = list.iterator (); while (elements.hasNext ()) { element = (String)elements.next (); if (element.length () != 0) { if (delimiter == null) delimiter = ", "; else list_string += delimiter; list_string += element; } } } return list_string; } private static String column_name_for_ResultSet ( String column_name, ResultSet result_set ) throws SQLException { if (column_name == null || result_set == null) return column_name; ResultSetMetaData metadata = result_set.getMetaData (); String name; int columns = metadata.getColumnCount (), count = 0; // N.B.: The first column is number 1. while (count++ < columns) if (column_name.equalsIgnoreCase (name = metadata.getColumnName (count))) return name; return null; } private static void report_ResultSetMetaData ( ResultSet results ) throws SQLException { ResultSetMetaData metadata = results.getMetaData (); int columns = metadata.getColumnCount (), count = 0; System.out.println (" ResultSetMetaData:" + NL +" " + columns + " columns -"); // N.B.: The first column is number 1. while (count++ < columns) System.out.println (" Column " + count + NL +" isCaseSensitive = " + metadata.isCaseSensitive (count) + NL +" ColumnLabel = " + metadata.getColumnLabel (count) + NL +" ColumnName = " + metadata.getColumnName (count) + NL +" SchemaName = " + metadata.getSchemaName (count) + NL +" TableName = " + metadata.getTableName (count)); } } // End of class pirl-2.3.8/PIRL/Database/Update_DB0000755000175000017500000001347011075011552016335 0ustar mathieumathieu#!/bin/csh -f # # Update_DB # # A wrapper for the PIRL Java Database updater. # # (see http://pirlwww.lpl.arizona.edu/software/PIRL_Java_Packages/PIRL/Database/Update_DB.html) # # Environment variables used and their default values: # # PIRL_JAVA_HOME - /opt/java # This may be a directory pathname where the PIRL subdirectory and # all its class files is located; or it may be the pathname to the # PIRL.jar file containing all dependencies. On Darwin systems the # ~/Library/Java/Extensions and /Library/Java/Extensions directory # will be checked for the PIRL.jar file. # # The following are used only if PIRL_JAVA_HOME is not a jar file. # # MySQL_JDBC - $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # The pathname to the MySQL JDBC driver jar file or the root # directory where its class files are located. # (http://dev.mysql.com/downloads/connector/j/) # # PostgreSQL_JDBC - $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. # (http://jdbc.postgresql.org/) # # JCM - $PIRL_JAVA_HOME/jcm/jcm_data.jar # The pathname to the Java Components for Mathematics jar file or # the root directory where its class files are located. # (http://math.hws.edu/javamath) # # CVS ID: Update_DB,v 1.1 2008/10/14 03:35:06 castalia Exp set PIRL_root = /opt/java set Darwin_Extensions = Library/Java/Extensions set MySQL_JDBC_dir = mysql-connector set MySQL_JDBC_jar = mysql-connector.jar set PostgreSQL_JDBC_dir = PostgreSQL set PostgreSQL_JDBC_jar = postgresql.jar set JCM_dir = jcm set JCM_jar = jcm_data.jar set OS = `uname -s` # Location of the PIRL Java Packages. if (! $?PIRL_JAVA_HOME) then if (-e $PIRL_root/PIRL) then set PIRL_JAVA_HOME = $PIRL_root else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = ~/$Darwin_Extensions/PIRL.jar else if (-e /$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = /$Darwin_Extensions/PIRL.jar endif endif if (! $?PIRL_JAVA_HOME) then echo "Set the PIRL_JAVA_HOME environment variable" echo "to the location of the PIRL Java Packages." exit 1 endif endif if (! -e $PIRL_JAVA_HOME) then echo "No such file or directory: $PIRL_JAVA_HOME" echo "The PIRL Java Packages are required." exit -1 endif set classpath = $PIRL_JAVA_HOME if ($PIRL_JAVA_HOME !~ *.jar) then # Database drivers: # Location of the MySQL JDBC driver. if (! $?MySQL_JDBC) then if (-e $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar) then set MySQL_JDBC = $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = ~/$Darwin_Extensions/$MySQL_JDBC_jar else if (-e /$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = /$Darwin_Extensions/$MySQL_JDBC_jar endif endif if ($?MySQL_JDBC) then set classpath = ${classpath}:$MySQL_JDBC endif else if (! -e $MySQL_JDBC) then echo "No such file or directory: $MySQL_JDBC" echo "MySQL database support is not available." unset MySQL_JDBC unsetenv MySQL_JDBC else set classpath = ${classpath}:$MySQL_JDBC endif endif # Location of the PostgreSQL JDBC driver. if (! $?PostgreSQL_JDBC) then if (-e $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar else if (-e /$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = /$Darwin_Extensions/$PostgreSQL_JDBC_jar endif endif if ($?PostgreSQL_JDBC) then set classpath = ${classpath}:$PostgreSQL_JDBC endif else if (! -e $PostgreSQL_JDBC) then echo "No such file or directory: $PostgreSQL_JDBC" echo "PostgreSQL database support is not available." unset PostgreSQL_JDBC unsetenv PostgreSQL_JDBC else set classpath = ${classpath}:$PostgreSQL_JDBC endif endif if (! $?MySQL_JDBC && ! $?PostgreSQL_JDBC) then echo "Database support is required." echo echo "One or both of the following will provide database support:" echo echo "Use the MySQL_JDBC environment variable" echo "to set the location of the $MySQL_JDBC_jar file" echo "or place it in the $PIRL_root/$MySQL_JDBC_dir directory." echo "See http://dev.mysql.com/downloads/connector/j/" echo echo "Use the PostgreSQL_JDBC environment variable" echo "to set the location of the $PostgreSQL_JDBC_jar file" echo "or place it in the $PIRL_root/$PostgreSQL_JDBC_dir directory." echo "See http://jdbc.postgresql.org/" Jar_Location_Note: if ($OS == "Darwin") then echo echo "Jar files may also be placed in the" echo "~/$Darwin_Extensions or" echo "/$Darwin_Extensions directories." endif echo exit -1 endif # External Java packages: # Location of the Java Components for Mathematics. if (! $?JCM) then if (-e $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar) then set JCM = $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$JCM_jar) then set JCM = ~/$Darwin_Extensions/$JCM_jar else if (-e /$Darwin_Extensions/$JCM_jar) then set JCM = /$Darwin_Extensions/$JCM_jar endif endif if (! $?JCM) then goto JCM_Note endif else if (! -e $JCM) then echo "No such file or directory: $JCM" JCM_Note: echo echo "The Java Components for Mathematics are required by Conductor." echo "Use the JCM environment variable" echo "to set the location of the $JCM_jar file" echo "or place it in the $PIRL_root/$JCM_dir directory." echo "See http://math.hws.edu/javamath" goto Jar_Location_Note endif endif set classpath = ${classpath}:$JCM endif exec java -cp $classpath PIRL.Database.Update_DB $argv:q pirl-2.3.8/PIRL/Database/Query_DB0000755000175000017500000001346511101457015016222 0ustar mathieumathieu#!/bin/csh -f # # Query_DB # # A wrapper for the PIRL Java Database updater. # # (see http://pirlwww.lpl.arizona.edu/software/PIRL_Java_Packages/PIRL/Database/Update_DB.html) # # Environment variables used and their default values: # # PIRL_JAVA_HOME - /opt/java # This may be a directory pathname where the PIRL subdirectory and # all its class files is located; or it may be the pathname to the # PIRL.jar file containing all dependencies. On Darwin systems the # ~/Library/Java/Extensions and /Library/Java/Extensions directory # will be checked for the PIRL.jar file. # # The following are used only if PIRL_JAVA_HOME is not a jar file. # # MySQL_JDBC - $PIRL_JAVA_HOME/mysql-connector/mysql-connector.jar # The pathname to the MySQL JDBC driver jar file or the root # directory where its class files are located. # (http://dev.mysql.com/downloads/connector/j/) # # PostgreSQL_JDBC - $PIRL_JAVA_HOME/PostgreSQL/postgresql.jar # The pathname to the PostgreSQL JDBC driver jar file or the root # directory where its class files are located. # (http://jdbc.postgresql.org/) # # JCM - $PIRL_JAVA_HOME/jcm/jcm_data.jar # The pathname to the Java Components for Mathematics jar file or # the root directory where its class files are located. # (http://math.hws.edu/javamath) # # CVS ID: Query_DB,v 3.1 2008/10/28 00:34:21 castalia Exp set PIRL_root = /opt/java set Darwin_Extensions = Library/Java/Extensions set MySQL_JDBC_dir = mysql-connector set MySQL_JDBC_jar = mysql-connector.jar set PostgreSQL_JDBC_dir = PostgreSQL set PostgreSQL_JDBC_jar = postgresql.jar set JCM_dir = jcm set JCM_jar = jcm_data.jar set OS = `uname -s` # Location of the PIRL Java Packages. if (! $?PIRL_JAVA_HOME) then if (-e $PIRL_root/PIRL) then set PIRL_JAVA_HOME = $PIRL_root else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = ~/$Darwin_Extensions/PIRL.jar else if (-e /$Darwin_Extensions/PIRL.jar) then set PIRL_JAVA_HOME = /$Darwin_Extensions/PIRL.jar endif endif if (! $?PIRL_JAVA_HOME) then echo "Set the PIRL_JAVA_HOME environment variable" echo "to the location of the PIRL Java Packages." exit 1 endif endif if (! -e $PIRL_JAVA_HOME) then echo "No such file or directory: $PIRL_JAVA_HOME" echo "The PIRL Java Packages are required." exit -1 endif set classpath = $PIRL_JAVA_HOME if ($PIRL_JAVA_HOME !~ *.jar) then # Database drivers: # Location of the MySQL JDBC driver. if (! $?MySQL_JDBC) then if (-e $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar) then set MySQL_JDBC = $PIRL_JAVA_HOME/$MySQL_JDBC_dir/$MySQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = ~/$Darwin_Extensions/$MySQL_JDBC_jar else if (-e /$Darwin_Extensions/$MySQL_JDBC_jar) then set MySQL_JDBC = /$Darwin_Extensions/$MySQL_JDBC_jar endif endif if ($?MySQL_JDBC) then set classpath = ${classpath}:$MySQL_JDBC endif else if (! -e $MySQL_JDBC) then echo "No such file or directory: $MySQL_JDBC" echo "MySQL database support is not available." unset MySQL_JDBC unsetenv MySQL_JDBC else set classpath = ${classpath}:$MySQL_JDBC endif endif # Location of the PostgreSQL JDBC driver. if (! $?PostgreSQL_JDBC) then if (-e $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = $PIRL_JAVA_HOME/$PostgreSQL_JDBC_dir/$PostgreSQL_JDBC_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = ~/$Darwin_Extensions/$PostgreSQL_JDBC_jar else if (-e /$Darwin_Extensions/$PostgreSQL_JDBC_jar) then set PostgreSQL_JDBC = /$Darwin_Extensions/$PostgreSQL_JDBC_jar endif endif if ($?PostgreSQL_JDBC) then set classpath = ${classpath}:$PostgreSQL_JDBC endif else if (! -e $PostgreSQL_JDBC) then echo "No such file or directory: $PostgreSQL_JDBC" echo "PostgreSQL database support is not available." unset PostgreSQL_JDBC unsetenv PostgreSQL_JDBC else set classpath = ${classpath}:$PostgreSQL_JDBC endif endif if (! $?MySQL_JDBC && ! $?PostgreSQL_JDBC) then echo "Database support is required." echo echo "One or both of the following will provide database support:" echo echo "Use the MySQL_JDBC environment variable" echo "to set the location of the $MySQL_JDBC_jar file" echo "or place it in the $PIRL_root/$MySQL_JDBC_dir directory." echo "See http://dev.mysql.com/downloads/connector/j/" echo echo "Use the PostgreSQL_JDBC environment variable" echo "to set the location of the $PostgreSQL_JDBC_jar file" echo "or place it in the $PIRL_root/$PostgreSQL_JDBC_dir directory." echo "See http://jdbc.postgresql.org/" Jar_Location_Note: if ($OS == "Darwin") then echo echo "Jar files may also be placed in the" echo "~/$Darwin_Extensions or" echo "/$Darwin_Extensions directories." endif echo exit -1 endif # External Java packages: # Location of the Java Components for Mathematics. if (! $?JCM) then if (-e $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar) then set JCM = $PIRL_JAVA_HOME/$JCM_dir/$JCM_jar else if ($OS == "Darwin") then if (-e ~/$Darwin_Extensions/$JCM_jar) then set JCM = ~/$Darwin_Extensions/$JCM_jar else if (-e /$Darwin_Extensions/$JCM_jar) then set JCM = /$Darwin_Extensions/$JCM_jar endif endif if (! $?JCM) then goto JCM_Note endif else if (! -e $JCM) then echo "No such file or directory: $JCM" JCM_Note: echo echo "The Java Components for Mathematics are required by Conductor." echo "Use the JCM environment variable" echo "to set the location of the $JCM_jar file" echo "or place it in the $PIRL_root/$JCM_dir directory." echo "See http://math.hws.edu/javamath" goto Jar_Location_Note endif endif set classpath = ${classpath}:$JCM endif exec java -cp $classpath PIRL.Database.Query_DB $argv:q pirl-2.3.8/PIRL/Database/Database_Exception.java0000644000175000017500000002330011742733571021213 0ustar mathieumathieu/* Database_Exception PIRL CVS ID: Database_Exception.java,v 1.18 2012/04/16 06:08:57 castalia Exp Copyright (C) 2001-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import PIRL.Strings.Words; import java.sql.*; import java.util.Vector; import PIRL.Configuration.Configuration_Exception; /** The excpetion thrown from the PIRL.Database package.

@author Bradford Castalia - UA/PIRL @version 1.18 */ public class Database_Exception extends java.lang.Exception { /** Class identification name with source code version and date. */ public static final String ID = "PIRL.Database.Database_Exception (1.18 2012/04/16 06:08:57)"; /** Delimiter characters for message words. */ private static final String DELIMITERS = " \t\n\r&:="; /** System new-line sequence. */ private static final String NL = Database.NL; /** The list of words that may preceed words in the message to be masked out. */ private static final Vector MASKED_WORDS = new Vector (1); static { MASKED_WORDS.add ("password"); MASKED_WORDS.add ("Password"); MASKED_WORDS.add ("PASSWORD"); } /** The two character "class" prefix of a five character SQLState value defined by the X/Open and SQL Access Group (Open Group) SQL CAE specification (1992) that indicates a failure to connect to the database. */ public static final String DISCONNECTED_STATE = "08"; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_CONSTRUCTOR = 1 << 0, DEBUG_ACCESSORS = 1 << 1, DEBUG_HELPERS = 1 << 2, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Database_Exception with a message and a cause.

The ID of this class will preceed the message. If a none-null message is specified it will be preceed with a new-line character. If a non-null cause is specified its message will be appended to the specified message after a new-line character.

Appending the cause's message to the specified message is done for backwards compatibility with the exceptions that did not support a chained cause. This can result in applications that expect to chain the cause messages themselves to get unexpected redundant messages.

N.B.: Once a cause is set it can not be changed. Therefore, though it is allowed to set the cause to null, the Database_Exception will not do this to avoid the situation of not being able to replace a null cause with a valid cause. Use the {@link Throwable#initCause(Throwable)} method to provide a cause after the Configuration_Exception has been constructed without one.

@param message The exception's message String (may be null). @param cause The exception's Throwable cause (may be null). If the cause is null, no cause is set in the exception object. */ public Database_Exception ( String message, Throwable cause ) { super (ID + ((message == null) ? "" : (NL + masked_String (message))) + ((cause == null) ? "" : (NL + exception_String (cause)))); if (cause != null) initCause (cause); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">-< Database_Exception: cause - " + ((cause instanceof SQLException) ? "SQLException" : ((cause instanceof Database_Exception) ? "Database_Exception" : ((cause == null) ? "none" : "Throwable")))); } /** Constructs a Database_Exception having a cause.

The exception will have the ID of this class as its message.

@param cause The exception's Throwable cause (may be null). */ public Database_Exception ( Throwable cause ) {this ((String)null, cause);} /** Constructs a Database_Exception with a message.

@param message The exception's message String (may be null). @see #Database_Exception(String, Throwable) */ public Database_Exception ( String message ) {this (message, null);} /** Constructs a Database_Exception.

The exception will have the ID of this class as its message.

@see #Database_Exception(String, Throwable) */ public Database_Exception () {this (null, null);} /*============================================================================== Methods */ /** Tests if this Database_Exception was caused by an SQLException indicating loss of database connection.

If the cause of the Database_Exception was itself a Database_Exception it's cause is recursivly checked.

@return true if this exception was caused by an SQLException which has an SQL state class (first two characters) of {@link #DISCONNECTED_STATE}. */ public boolean Disconnected () { Throwable cause = getCause (); while (cause != null && cause instanceof Database_Exception) cause = cause.getCause (); if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.println (">-< Database_Exception.Disconnected:" + ((cause instanceof SQLException) ? (" " + ((SQLException)cause).getSQLState ().startsWith (DISCONNECTED_STATE) + " (" + ((SQLException)cause).getSQLState () + ')') : " false")); return cause instanceof SQLException && ((SQLException)cause).getSQLState ().startsWith (DISCONNECTED_STATE); } /*============================================================================== Helpers */ /** Generates a message String for a Throwable.

The String returned by the {@link Throwable#getMessage()} method is used. If the throwable is null or its message is null, the empty String is used. This String has words that should be hidden {@link #masked_String(String) masked} out.

If the throwable is a SQLException its message is preceed with a "SQL exception -" line and appended with two lines listing the SQL state and error codes of the exception.

@param throwable The Throwable from which to generate a message. */ static public String exception_String ( Throwable throwable ) { String message = ""; while (throwable != null) { if ((throwable instanceof SQLException) && message.length () == 0) message = "SQL exception -" + NL; message += masked_String (throwable.getMessage ()); if (throwable instanceof SQLException) { message += " State Code: " + ((SQLException)throwable).getSQLState () + NL + " Error Code: " + ((SQLException)throwable).getErrorCode (); if (((SQLException)throwable).getNextException () != null) throwable = ((SQLException)throwable).getNextException (); else throwable = ((SQLException)throwable).getCause (); } else throwable = throwable.getCause (); } if ((DEBUG & DEBUG_HELPERS) != 0) System.out.println (">-< Database_Exception.exception_String:" + NL + message); return message; } /** Generates a message String for an Exception.

@param exception The Exception from which to generate a message. @see #exception_String(Throwable) */ static public String exception_String ( Exception exception ) {return exception_String ((Throwable)exception);} /** Masks out words that should be hidden in a String.

When the word "password", "Password" or "PASSWORD" is encountered in the string the following word is replaced with "*******". The delimiters used to identify word characters are any of the " \t\n\r&:=" characters.

@param string The String to be masked. @return The masked String. @see Words#Mask(Vector) @see Words#Delimiters(String) */ static public String masked_String ( String string ) { if (string == null) return ""; Words words = new Words (string); return words .Delimiters (DELIMITERS) .Mask (MASKED_WORDS) .toString (); } /** Copy an SQLException with any passwords masked out.

A new SQLException is constructed containing a copy of the message and state description from the specified SQLException with any passwords {@link #masked_String(String) masked out} in the message. The stack track is also copied. If the SQLException has a chained cause, that is copied over.

This method is applied recursively to any SQLException chained to the specified exception, and the resulting new SQLException used to replace the chained exception.

In effect, a deep copy of the specified SQLException is made with the message being masked in the process.

@param SQL_exception The SQLException to be masked and copied. @return A SQLExcpetion that is a deep copy of the original. */ public static SQLException masked_SQLException ( SQLException SQL_exception ) { if (SQL_exception == null) return null; // Message. String message = SQL_exception.getMessage (); if (message != null) message = Database_Exception.masked_String (message); SQLException exception = new SQLException (message, SQL_exception.getSQLState ()); // Stack trace. exception.setStackTrace (SQL_exception.getStackTrace ()); // Chained Throwable. Throwable throwable = SQL_exception.getCause (); if (throwable != null) exception.initCause (throwable); // Chained SQLException. SQLException chained_exception = SQL_exception.getNextException (); if (chained_exception != null) SQL_exception.setNextException (masked_SQLException (chained_exception)); return exception; } } // End of class pirl-2.3.8/PIRL/Database/Fields_Map.java0000644000175000017500000006454111742733571017510 0ustar mathieumathieu/* Fields_Map PIRL CVS ID: Fields_Map.java,v 1.13 2012/04/16 06:08:57 castalia Exp Copyright (C) 2005-2007 Arizona Board of Regents on behalf of the Planetary Image Research Laboratory, Lunar and Planetary Laboratory at the University of Arizona. This file is part of the PIRL Java Packages. The PIRL Java Packages are free software; you can redistribute them and/or modify them under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The PIRL Java Packages are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . *******************************************************************************/ package PIRL.Database; import java.util.List; import java.util.Vector; import java.util.Iterator; import java.util.Arrays; /** A Fields_Map maps a list of field names in user specified order to the actual order of field names as returned from a database table query.

The Fields_Map relates two possible orderings of a list of field names: The actual order is the order of entries as they occur before being mapped. This is expected to be the order in which table record field entries are returned from a database query. The user order is the the order in which the user of table records expects the entries to be in after being mapped. The user order might be thought of as the logical order known to the user of the map as typically set by by pre-determined constant index values associated with known record field names.

The map is constructed by providing a list of field names in user order and a list of field names in actual order. Every user field name that is found in the actual field names list results in a mapping of that name from its index in the user order to its index in the actual order. User names that are not found in the list of actual field names are mapped to the no-index value. Field names are {@link Database#Matches(String, String, boolean) matched} using either case sensitive or case insensitive comparison.

The map may be used to access a field entry from a record in actual order by specifying a field name or a user index.

@author Bradford Castalia, Drew Castalia - idaeim studio @version 1.13 */ public class Fields_Map { public static final String ID = "PIRL.Database.Fields_Map (1.13 2012/04/16 06:08:57)"; /** The default for case sensitive field name matches.

The initial value is false; */ public static boolean Case_Sensitive_Default = false; /** Whether to use case sensitive field name matches.

: The case sensistive matching condition is set when the Field_Map is constructed and used by the constructor to assemble the map of user field name indexes to actual field name indexes. This condition is also used for {@link #index(String) user field name to actual field index} and {@link #entry(List, String) user field name to table record entry} mapping. Changing the condition after the Field_Map has been constructed may produce inconsistent mapping results. */ public boolean Case_Sensitive; // Original field names as provided to the constructor. private String User_Fields[] = {}, Actual_Fields[] = {}; // Array of actual field index values in user index order. private int User_to_Actual_Index_Map[] = {}; // User_to_Actual_Index_Map sorted with only non-negative unique values. private int Actual_Ordered_Index_Map[] = null; private static final String NL = Database.NL; // Debug control. private static final int DEBUG_OFF = 0, DEBUG_SETUP = 1 << 0, DEBUG_CONSTRUCTOR = 1 << 1, DEBUG_ACCESSORS = 1 << 2, DEBUG_UTILITY = 1 << 3, DEBUG_ALL = -1, DEBUG = DEBUG_OFF; /*============================================================================== Constructors */ /** Constructs a Fields_Map that maps a List of field names in user index order to their index in a List of field names in actual (table record) order.

User field names that are not found in the List of actual names map to a -1 index to indicate missing fields.

User field names are compared to actual field names using the {@link #Case_Sensitive_Default default case sensitivity}.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields A List of user field name Strings to be mapped. @param actual_fields A List of field name Strings in actual (record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @param case_sensitive If true, user field names are compared to actual field names with a case sensitive match; otherwise a case insensitive match is used. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( List user_fields, List actual_fields, boolean case_sensitive ) { /* Programmer's note: The effective constructor uses List arguments rather than array arguments because the Arrays.asList method used by constructors on arrays does not copy the orginal array, whereas the Collection.toArray method copies the elements of the Collection into the array. Since the working constructor is always going to make a local copy the field lists, using a List backed by a user specified array avoids unnecessary additional copying. */ if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (">>> Fields_Map:" + NL +" user_fields - " + user_fields + NL +" actual_fields - " + actual_fields + NL +" case_sensitive - " + case_sensitive); Case_Sensitive = case_sensitive; // Copy the fields lists. if (user_fields != null) user_fields.toArray (User_Fields = new String[user_fields.size ()]); if (actual_fields != null) actual_fields.toArray (Actual_Fields = new String[actual_fields.size ()]); if (user_fields == null || actual_fields == null) throw new IllegalArgumentException (ID + NL + "Can't construct a Fields_Map with null arguments."); // Assemble the user-to-actual indexes map. int index = User_Fields.length; User_to_Actual_Index_Map = new int[index]; while (index-- > 0) { User_to_Actual_Index_Map[index] = Database.Index (Actual_Fields, User_Fields[index], Case_Sensitive); if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println (" " + index + ':' + User_Fields[index] + " -> " + User_to_Actual_Index_Map[index] + ((User_to_Actual_Index_Map[index] < 0) ? "" : (":" + Actual_Fields[User_to_Actual_Index_Map[index]]))); } if ((DEBUG & DEBUG_CONSTRUCTOR) != 0) System.out.println ("<<< Fields_Map"); } /** Constructs a Fields_Map that maps a List of field names in user index order to their index in a List of field names in actual (table record) order.

User field names that are not found in the List of actual names map to a -1 index to indicate missing fields.

User field names are compared to actual field names using the {@link #Case_Sensitive_Default default case sensitivity}.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields A List of user field name Strings to be mapped. @param actual_fields A List of field name Strings in actual (record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( List user_fields, List actual_fields ) {this (user_fields, actual_fields, Case_Sensitive_Default);} /** Constructs a Fields_Map that maps an array of field names in user index order to their index in an array of field names in actual (table record) order.

User field names that are not found in the array of actual names map to a -1 index to indicate missing fields.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields An array of user field name Strings to be mapped. @param actual_fields An array of field name Strings in actual (tablae record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @param case_sensitive If true, user field names are compared to actual field names with a case sensitive match; otherwise a case insensitive match is used. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( String[] user_fields, String[] actual_fields, boolean case_sensitive ) { this ((user_fields == null) ? (List)null : Arrays.asList (user_fields), (actual_fields == null) ? (List)null : Arrays.asList (actual_fields), case_sensitive); } /** Constructs a Fields_Map that maps an array of field names in user index order to their index in an array of field names in actual (table record) order.

User field names that are not found in the array of actual names map to a -1 index to indicate missing fields.

User field names are compared to actual field names using the {@link #Case_Sensitive_Default default case sensitivity}.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields An array of user field name Strings to be mapped. @param actual_fields An array of field name Strings in actual (table record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( String[] user_fields, String[] actual_fields ) { this ((user_fields == null) ? (List)null : Arrays.asList (user_fields), (actual_fields == null) ? (List)null : Arrays.asList (actual_fields), Case_Sensitive_Default); } /** Constructs a Fields_Map that maps an array of field names in user index order to their index in a List of field names in actual (table record) order.

User field names that are not found in the List of actual names map to a -1 index to indicate missing fields.

User field names are compared to actual field names using the {@link #Case_Sensitive_Default default case sensitivity}.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields An array of user field name Strings to be mapped. @param actual_fields A List of field name Strings in actual (record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( String[] user_fields, List actual_fields ) { this ((user_fields == null) ? (List)null : Arrays.asList (user_fields), actual_fields, Case_Sensitive_Default); } /** Constructs a Fields_Map that maps a List of field names in user index order to their index in an array of field names in actual (table record) order.

User field names that are not found in the array of actual names map to a -1 index to indicate missing fields.

User field names are compared to actual field names using the {@link #Case_Sensitive_Default default case sensitivity}.

N.B.: The first actual field name that matches each user field name provides the mapping. If the actual field names list contains duplicate entries, only the first entry will be mapped. Likewise, if case insensitive matching is being done only the first actual name that matches a user name regardless of case is mapped.

N.B.: A field names list may contain null entries. A null name always matches a null name. A non-null name never matches a null name.

@param user_fields A List of user field name Strings to be mapped. @param actual_fields An array of field name Strings in actual (record) order; as returned in the first record of a {@link Database#Select(String, String) Database.Select} operation. @throws IllegalArgumentException If either argument is null. */ public Fields_Map ( List user_fields, String[] actual_fields ) { this (user_fields, (actual_fields == null) ? (List)null : Arrays.asList (actual_fields), Case_Sensitive_Default); } /*============================================================================== Accessors */ /** Maps a user field number to an actual record field number.

@param user_field_number The user field number to map. @return The actual record field number. This will be -1 if the user field_number is not within the range of the number of field names provided when the Fields_Map was constructed, or it maps to a field that was not present when the map was constructed. */ public int index ( int user_field_number ) { if (user_field_number >= 0 && user_field_number < User_to_Actual_Index_Map.length) return User_to_Actual_Index_Map[user_field_number]; return -1; } /** Maps a user field name to an actual field number.

The specified field name is compared to the list of user field names with the {@link #Case_Sensitive case sensitivity} of the Field_Map object.

@param user_field_name The user field name to map. @return The actual field number. This will be -1 if the field_name was not matched in the list of user field names provided when the Fields_Map was constructed, or it was not mapped to an actual field name. */ public int index ( String user_field_name ) { return index (Database.Index (User_Fields, user_field_name, Case_Sensitive)); } /** Gets a field entry from a record corresponding to a user field number.

The field number is mapped to is actual {@link #index(int) index}. If this results in a valid record index (non-negative less than the size of the record) the corresponding entry from the record is returned as a String.

@param record The record List containing field value Strings. If null, null is returned. @param field_number The user field number before mapping to its actual field index. @return The record List String entry for the mapped user field_number. This will be null if the user field number does not map to a valid record entry. */ public String entry ( List record, int field_number ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.print (">>> Fields_Map.entry:" + NL +" record - " + record + NL +" field_number - " + field_number); String field_value = null; if (record != null) { field_number = index (field_number); if (field_number >= 0 && field_number < record.size ()) field_value = record.get (field_number); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.print ("<<< Fields_Map.entry: " + field_value); return field_value; } /** Gets a field entry from a record corresponding to a user field name.

The field name is mapped to its actual {@link #index(String) index}. If this results in a valid record index (non-negative less than the size of the record) the corresponding entry from the record is returned as a String.

@param record The record List containing field value Strings. If null, null is returned. @param field_name The user field name of the field for which to get a record entry. @return The record List String entry for the mapped field_name. This will be null if the field name does not map to a valid record entry. */ public String entry ( List record, String field_name ) { if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.print (">>> Fields_Map.entry:" + NL +" record - " + record + NL +" field_name - " + field_name); String field_value = null; if (record != null) { int field_number = index (field_name); if (field_number >= 0 && field_number < record.size ()) field_value = (String)record.get (field_number); } if ((DEBUG & DEBUG_ACCESSORS) != 0) System.out.print ("<<< Fields_Map.entry: " + field_value); return field_value; } /** Gets a Vector of field names in user index order.

Only field names that were mapped when the Fields_Map was constructed will be included in the Vector that is returned.

@return A Vector of field names in user index order. */ public Vector User_Fields () { Vector user_fields = new Vector (); for (int index = 0; index < User_to_Actual_Index_Map.length; index++) if (User_to_Actual_Index_Map[index] >= 0) user_fields.add (User_Fields[index]); return user_fields; } /** Gets a Vector of all the user field names.

All the original user field names, as specified in the constructor, are included in the Vector that is returned.

@return A Vector that is a copy of the original user field names specified in the constructor. This will be empty if the constructor was given a null user fields list. */ public Vector All_User_Fields () {return new Vector (Arrays.asList (User_Fields));} /** Gets a Vector of field names in actual index order.

Only actual field names that were mapped from user field names when the Fields_Map was constructed will be included in the Vector that is returned. Thus the returned Vector may be smaller than the number of entries in the actual fields name list provided to the constructor.

@return A Vector of field names in actual index order. */ public Vector Actual_Fields () { int index; if (Actual_Ordered_Index_Map == null) { // Copy the index map. index = User_to_Actual_Index_Map.length; int array[] = new int[index]; while (index-- > 0) array[index] = User_to_Actual_Index_Map[index]; // Sort the copied index map into ascending order. Arrays.sort (array); // Count the number of non-negative, unique values. int count = 0, previous = -1; // Initial previous value is non-mapped. for (index = 0; index < array.length; index++) { if (array[index] != previous) { // Count the non-negative, unique value. ++count; previous = array[index]; } else // Duplicate or non-mapped to be excluded. array[index] = -1; } if (count == array.length) // No excluded values. Actual_Ordered_Index_Map = array; else { // Remove excluded values. Actual_Ordered_Index_Map = new int[count]; for (count = 0, index = 0; index < array.length; index++) if (array[index] >= 0) Actual_Ordered_Index_Map[count++] = array[index]; } } Vector actual_fields = new Vector (Actual_Ordered_Index_Map.length); for (index = 0; index < Actual_Ordered_Index_Map.length; index++) actual_fields.add (Actual_Fields[Actual_Ordered_Index_Map[index]]); return actual_fields; } /** Gets a Vector of actual field names in unmapped order.

All the original actual field names, as specified in the constructor, are included in the Vector that is returned.

@return A Vector that is a copy of the original actual field names specified in the constructor. This will be an empty if the constructor had a null user or actual fields list. */ public Vector All_Actual_Fields () {return new Vector (Arrays.asList (Actual_Fields));} /** Gets a Vector of actual field names in unmapped order.

This is a deprecated name for the {@link #All_Actual_Fields()} method.

@return A Vector that is a copy of the original actual field names specified in the constructor. This will be an empty if the constructor had a null useer or actual fields list. @deprecated */ public Vector Table_Fields () {return All_Actual_Fields ();} /** Gets a Vector of unmapped actual field names.

@return A Vector containing the actual field names are not mapped to user field names. */ public Vector Unmapped_Actual_Fields () { if (Actual_Ordered_Index_Map == null) Actual_Fields (); Vector fields = All_Actual_Fields (); int index = Actual_Ordered_Index_Map.length; while (--index >= 0) fields.remove (Actual_Ordered_Index_Map[index]); return fields; } /** Remaps a record from actual table record order to user fields order.

A new record is constructed from {@link #entry(List, int) mapped entries} of the specified record for all user fields. Any user field that is not mapped to an actual field results in a null record entry. Entries of the source record that are not mapped will not be included in the returned record; the returned record will be the same size as the user field names list that was used to construct the Fields_Map.

@param record A Vector of field values in actual order. If null, null is returned. @return A Vector of mapped record field values in user order. @throws IllegalArgumentException If the source record has fewer entries than the number of user fields. */ public Vector Actual_to_User ( Vector record ) { Vector mapped = null; if (record != null) { if (record.size () < User_to_Actual_Index_Map.length) throw new IllegalArgumentException (ID + NL + "The record to map from actual to user order only has " + record.size () + " of the required " + User_to_Actual_Index_Map.length + " fields."); mapped = new Vector (User_to_Actual_Index_Map.length); for (int index = 0; index < User_to_Actual_Index_Map.length; index++) mapped.add (entry (record, index)); } return mapped; } /** Gets the description of this Fields_Map.

@return A String describing this Fields_Map. */ public String toString () { String description = ID + NL + " User <- Actual"; int actual_index, user_index = -1; while (++user_index < User_Fields.length) { actual_index = index (User_Fields[user_index]); description += NL + String.format ("%8d <- %2d: %s", user_index, actual_index, User_Fields[user_index]); } Vector unmapped_fields = Unmapped_Actual_Fields (); if (unmapped_fields.size () > 0) { description += NL + " Unmapped -"; String name; int index = -1, size = unmapped_fields.size (); while (++index < size) { name = unmapped_fields.get (index); actual_index = -1; while (++actual_index < Actual_Fields.length) { if ((name == null && Actual_Fields[actual_index] == null) || (name != null && name.equals (Actual_Fields[actual_index]))) { description += NL + String.format ("%14d: %s", actual_index, Actual_Fields[actual_index]); break; } } } } return description; } /*============================================================================== Utility */ /** Confirms that a list of field names are all mapped to valid record locations.

Each name in the list is treated as a user field name that is required to be mapped to an actual field name. Thus the failure of any name in the list to be mapped will throw an exception. Nevertheless, all unmapped field names in the list are identified.

@param field_names An array of user name Strings to be checked. null entries are ignored. @throws Database_Exception if any names fail to map to valid record locations. The exception message will list all missing field names. */ public void Confirm_Fields ( String[] field_names ) throws Database_Exception {Confirm_Fields ((field_names == null) ? (List)null : Arrays.asList (field_names));} /** Confirms that a list of user field names are all mapped to valid actual fields.

Each name in the list is treated as a user field name that is required to be mapped to an actual field name. Thus the failure of any name in the list to be mapped will throw an exception. Nevertheless, all unmapped field names in the list are identified.

@param field_names The list of names to be checked. If null, nothing is done. Any null entries in the list are ignored. @throws Database_Exception if any of the field names fail to map to valid actual locations. The exception message will list all unmapped field names. */ public void Confirm_Fields ( List field_names ) throws Database_Exception { if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (">>> Fields_Map.Confirm_Fields"); if (field_names == null) return; String missing_fields = ""; Iterator names = field_names.iterator (); while (names.hasNext ()) { String name = (String)names.next (); if (name == null) continue; if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println (" " + name + " -> " + index (name)); if (index (name) < 0) missing_fields += ((missing_fields.length () == 0) ? "Missing required fields: " : ", ") + name; } if (missing_fields.length () != 0) throw new Database_Exception (ID + NL + missing_fields); if ((DEBUG & DEBUG_UTILITY) != 0) System.out.println ("<<< Fields_Map.Confirm_Fields"); } } // End of Fields_Map class.