[med-svn] [libbrowserlauncher-java] 01/05: Imported Upstream version 1.3+dfsg

Andreas Tille tille at debian.org
Mon Jan 18 10:31:15 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository libbrowserlauncher-java.

commit 7187c71699e118e0bc2ba784a64f057b3d639fc4
Author: Andreas Tille <tille at debian.org>
Date:   Mon Jan 18 10:54:02 2016 +0100

    Imported Upstream version 1.3+dfsg
---
 .classpath                                         |   6 +
 .project                                           |  17 +
 COPYING.txt                                        | 458 +++++++++++
 META-INF/INDEX.LIST                                |  56 ++
 META-INF/MANIFEST.MF                               |   5 +
 README.txt                                         |  85 ++
 build.xml                                          |  81 ++
 .../ejalbert/browserprefui/BrowserPrefs.properties |  26 +
 .../launching/misc/linuxUnixConfig.properties      |  33 +
 .../ejalbert/launching/misc/sunOSConfig.properties |  32 +
 .../launching/windows/windowsConfig.properties     |  48 ++
 .../ejalbert/resources/Debugging.properties        |  59 ++
 source/at/jta/NotSupportedOSException.java         |  28 +
 source/at/jta/RegistryErrorException.java          |  31 +
 source/at/jta/Regor.java                           | 741 ++++++++++++++++++
 source/edu/stanford/ejalbert/BrowserLauncher.java  | 407 ++++++++++
 .../stanford/ejalbert/BrowserLauncherRunner.java   | 144 ++++
 .../ejalbert/browserprefui/BrowserPrefAction.java  | 104 +++
 .../ejalbert/browserprefui/BrowserPrefDialog.java  | 130 ++++
 .../ejalbert/browserprefui/BrowserPrefs.properties |  26 +
 .../BrowserLaunchingExecutionException.java        |  40 +
 .../BrowserLaunchingInitializingException.java     |  39 +
 .../UnsupportedOperatingSystemException.java       |  40 +
 .../BrowserLauncherDefaultErrorHandler.java        |  44 ++
 .../BrowserLauncherErrorHandler.java               |  41 +
 .../ejalbert/launching/BrowserDescription.java     |  45 ++
 .../launching/BrowserLaunchingFactory.java         | 127 +++
 .../ejalbert/launching/IBrowserLaunching.java      | 177 +++++
 .../launching/macos/MacOs2_0BrowserLaunching.java  | 153 ++++
 .../launching/macos/MacOs2_1BrowserLaunching.java  | 144 ++++
 .../launching/macos/MacOs3_0BrowserLaunching.java  | 110 +++
 .../launching/macos/MacOs3_1BrowserLaunching.java  |  76 ++
 .../launching/macos/MacOsBrowserLaunching.java     | 113 +++
 .../launching/misc/StandardUnixBrowser.java        | 202 +++++
 .../launching/misc/SunOSBrowserLaunching.java      |  98 +++
 .../ejalbert/launching/misc/UnixBrowser.java       |  61 ++
 .../misc/UnixNetscapeBrowserLaunching.java         | 375 +++++++++
 .../launching/misc/linuxUnixConfig.properties      |  33 +
 .../ejalbert/launching/misc/sunOSConfig.properties |  32 +
 .../ejalbert/launching/utils/LaunchingUtils.java   |  55 ++
 .../ejalbert/launching/windows/WindowsBrowser.java | 147 ++++
 .../launching/windows/WindowsBrowserLaunching.java | 857 +++++++++++++++++++++
 .../launching/windows/windowsConfig.properties     |  48 ++
 .../ejalbert/resources/Debugging.properties        |  59 ++
 .../ejalbert/testing/BrowserLauncherTestApp.java   | 349 +++++++++
 .../stanford/ejalbert/testing/TestAppLogger.java   | 107 +++
 source/net/sf/wraplog/AbstractLogger.java          | 167 ++++
 source/net/sf/wraplog/Level.java                   |  76 ++
 source/net/sf/wraplog/Logger.java                  |  70 ++
 source/net/sf/wraplog/LoggingException.java        |  46 ++
 source/net/sf/wraplog/NoneLogger.java              |  52 ++
 source/net/sf/wraplog/SystemLogger.java            | 101 +++
 52 files changed, 6601 insertions(+)

diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..6a7b37f
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="source"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="output" path="classes"/>
+</classpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000..0a929c6
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>BrowserLauncher2</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/COPYING.txt b/COPYING.txt
new file mode 100644
index 0000000..3b20440
--- /dev/null
+++ b/COPYING.txt
@@ -0,0 +1,458 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+

+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+

+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+

+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+

+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+

+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+

+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+

+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+

+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
diff --git a/META-INF/INDEX.LIST b/META-INF/INDEX.LIST
new file mode 100644
index 0000000..24c51b5
--- /dev/null
+++ b/META-INF/INDEX.LIST
@@ -0,0 +1,56 @@
+JarIndex-Version: 1.0
+
+BrowserLauncher2-all-1_3.jar
+at
+at/jta
+classes
+classes/edu
+classes/edu/stanford
+classes/edu/stanford/ejalbert
+classes/edu/stanford/ejalbert/browserprefui
+classes/edu/stanford/ejalbert/launching
+classes/edu/stanford/ejalbert/launching/misc
+classes/edu/stanford/ejalbert/launching/windows
+classes/edu/stanford/ejalbert/resources
+edu
+edu/stanford
+edu/stanford/ejalbert
+edu/stanford/ejalbert/browserprefui
+edu/stanford/ejalbert/exception
+edu/stanford/ejalbert/exceptionhandler
+edu/stanford/ejalbert/launching
+edu/stanford/ejalbert/launching/macos
+edu/stanford/ejalbert/launching/misc
+edu/stanford/ejalbert/launching/utils
+edu/stanford/ejalbert/launching/windows
+edu/stanford/ejalbert/resources
+edu/stanford/ejalbert/testing
+net
+net/sf
+net/sf/wraplog
+package cache
+source
+source/at
+source/at/jta
+source/edu
+source/edu/stanford
+source/edu/stanford/ejalbert
+source/edu/stanford/ejalbert/browserprefui
+source/edu/stanford/ejalbert/exception
+source/edu/stanford/ejalbert/exceptionhandler
+source/edu/stanford/ejalbert/launching
+source/edu/stanford/ejalbert/launching/macos
+source/edu/stanford/ejalbert/launching/misc
+source/edu/stanford/ejalbert/launching/utils
+source/edu/stanford/ejalbert/launching/windows
+source/edu/stanford/ejalbert/resources
+source/edu/stanford/ejalbert/testing
+source/net
+source/net/sf
+source/net/sf/wraplog
+.classpath
+.project
+COPYING.txt
+README.txt
+build.xml
+
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..959ed9c
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Ant-Version: Apache Ant 1.6.5
+Created-By: 1.4.2_04-b05 (Sun Microsystems Inc.)
+Main-Class: edu/stanford/ejalbert/testing/BrowserLauncherTestApp
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..8326cb6
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,85 @@
+$Id: README.txt,v 1.4 2007/08/30 19:44:04 jchapman0 Exp $
+
+Introduction
+------------
+
+BrowserLauncher2, a continuation of the BrowserLauncher project, 
+is a library that facilitates openning a browser from a Java 
+application and directing the browser to a supplied url. In 
+most cases the browser openned will be the user's default browser.
+
+BrowserLauncher2 has been released under the LGPL. 
+Read the COPYING.txt file for licensing information.
+
+Project Founder (BrowserLauncher): 
+Eric Albert
+
+Project Founder (BrowserLauncher2): 
+Jeff Chapman sdvalidator at yahoo.com
+
+Contributors:  
+Thomas Aglassinger
+Jeff Chapman
+Chris Dance
+Markus Gebhard
+Olivier Hochreutiner
+Morgan Schweers
+
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+For more information on OSI, see http://www.opensource.org
+
+System Requirements
+-------------------
+
+BrowserLauncher2 is written entirely in Java. The libraries have been 
+compiled using JDK 1.4. Operating System support is ongoing. The library 
+supports various flavors of Mac, Windows, and Unix/Linux.
+
+Using the Library
+-----------------
+
+The preferred method for using the BrowserLauncher2 api is to create an 
+instance of BrowserLauncher (edu.stanford.ejalbert.BrowserLauncher) and 
+invoke the method: public void openURLinBrowser(String urlString).
+
+If the application will be invoking urls often, it might be useful to wrap 
+the BrowserLauncher instance with a singleton or use some mechanism to cache it.
+
+The call to openURLinBrowser() should be executed in a separate thread from 
+the application's main/event thread. Applications can create an instance of 
+BrowserLauncherRunner (edu.stanford.ejalbert.BrowserLauncherRunner) and pass 
+it to a Thread. The sample code below is taken from the test application 
+(edu.stanford.ejalbert.BrowserLauncher.BrowserLauncherTestApp) which can be 
+used as a reference application.
+
+BrowserLauncherErrorHandler errorHandler = new TestAppErrorHandler(debugTextArea);
+BrowserLauncherRunner runner = new BrowserLauncherRunner(launcher, urlString, errorHandler);
+Thread launcherThread = new Thread(runner);
+launcherThread.start();
+
+Third Party Libraries:
+
+BrowserLauncher2 uses two libraries: WrapLog and Pure Java registry wrapper for Windows.  
+The code for these libraries is integrated into the BrowserLauncher2 build.
+
+Logging
+---------
+BrowserLauncher2 uses a subset of WrapLog 1.1 for logging. If you do not specify a logger 
+instance, a default logger (NoneLogger) will be used. The default logger does not log 
+anything.
+
+WrapLog is available under the BSD License.
+
+For more information on using WrapLog, see http://sourceforge.net/projects/wraplog.
+
+Windows Registry Access
+-----------------------
+BrowserLauncher2 uses Pure Java registry wrapper for Windows (release/version 2.0) for
+reading the Windows registry to locate browsers on a user's system.
+
+Pure Java registry wrapper for Windows is available under the GNU Library or Lesser 
+General Public License (LGPL).
+
+For more information on using Pure Java registry wrapper for Windows, see 
+http://sourceforge.net/projects/java-registry.
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..0bbf084
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!-- Build file for BrowserLauncher2 -->
+<!-- $Id: build.xml,v 1.9 2007/08/30 19:47:37 jchapman0 Exp $ -->
+<project name="BrowserLauncher2" default="build" basedir=".">
+    <property name="release.number" value="1_3" /> <!-- 1.3 -->
+    <property name="product.name" value="BrowserLauncher2" />
+    <property name="output.dir" value="classes" />
+    <property name="source.dir" value="source" />
+    <property name="lib.dir" value="lib" />
+    <property name="deployment.dir" value="deployment" />
+    <property name="api.dir" value="api" />
+    <property name="archive.runtime" value="${deployment.dir}/BrowserLauncher2-${release.number}.jar" />
+    <property name="archive.all" value="${deployment.dir}/BrowserLauncher2-all-${release.number}.jar" />
+    <property name="optimize" value="on" />
+    <property name="app.manifest" value="${deployment.dir}/manifest.mf" />
+    <property name="app.manifest.test" value="${deployment.dir}/manifest.test.mf" />
+    <property name="gnu.copying" value="${deployment.dir}/COPYING.txt" />
+    <property name="readme" location="${deployment.dir}/README.txt" />
+
+    <target name="init">
+        <tstamp />
+        <mkdir dir="${lib.dir}" />
+        <mkdir dir="${output.dir}" />
+        <mkdir dir="${deployment.dir}" />
+    </target>
+
+    <target name="clean-deploy" description="does clean then deploy">
+      <antcall target="clean"/>
+      <antcall target="deploy"/>
+    </target>
+
+    <target name="clean" description="remove all files created by build" depends="init">
+        <delete>
+            <fileset dir="${output.dir}" />
+        </delete>
+        <delete dir="${api.dir}" />
+        <delete file="${archive.all}" />
+        <delete file="${archive.runtime}" />
+    </target>
+
+    <target name="build" depends="init" description="compile all source files">
+        <mkdir dir="${output.dir}" />
+        <javac srcdir="${source.dir}" destdir="${output.dir}" debug="true" />
+		<!-- copy all non-java files (properties) -->
+		<copy todir="${output.dir}">
+			<fileset dir="${source.dir}" excludes="**/*.java"/>
+		</copy>
+        <jar destfile="${archive.runtime}" basedir="${output.dir}" includes="**/*.class" />
+    </target>
+
+    <target name="deploy" depends="build" description="create archives">
+        <jar destfile="${archive.runtime}"
+               index="true"
+               update="overwrite"
+               manifest="${app.manifest}" >
+               <fileset dir="${output.dir}" />
+               <fileset file="${gnu.copying}" />
+               <fileset file="${readme}" />
+           </jar>
+        <jar destfile="${archive.all}"
+	           index="true"
+	           update="overwrite"
+	           manifest="${app.manifest.test}" >
+	           <fileset dir="${output.dir}" />
+	           <fileset dir="." includes="build.xml, *.txt" />
+	           <fileset dir="." includes=".classpath, .project" />
+	           <fileset dir="." includes="**/*.java" />
+	           <fileset dir="." includes="**/*.properties" />
+	           <fileset dir="." includes="${api.dir}/**" />
+	           <fileset file="${gnu.copying}" />
+	           <fileset file="${readme}" />
+        </jar>
+    </target>
+
+    <target name="api" depends="init" description="create API documentation">
+        <javadoc destdir="${api.dir}" windowtitle="${product.name}" link="http://java.sun.com/j2se/1.4.2/docs/api/" author="true" version="true" package="true">
+            <packageset dir="${source.dir}" />
+        </javadoc>
+    </target>
+
+</project>
diff --git a/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties b/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties
new file mode 100644
index 0000000..8a9bf91
--- /dev/null
+++ b/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties
@@ -0,0 +1,26 @@
+# ************************************************
+#    Copyright 2006 Jeff Chapman
+
+#    This file is part of BrowserLauncher2.
+
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# ************************************************/
+# $Id: BrowserPrefs.properties,v 1.1 2006/09/26 19:40:41 jchapman0 Exp $
+
+dialog.title=Browser Preference
+
+dialog.bttn.ok=OK
+dialog.bttn.cancel=Cancel
diff --git a/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties b/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties
new file mode 100644
index 0000000..ff858fb
--- /dev/null
+++ b/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties
@@ -0,0 +1,33 @@
+# ************************************************
+#    Copyright 2006,2007 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: linuxUnixConfig.properties,v 1.3 2007/06/13 19:25:54 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# list of browsers and arguments for using them
+# display name | executable name | start browser args | invoke already started browser | force new window
+browser.mozilla=Mozilla;mozilla;<browser> <url>;<browser> -remote openURL(<url>)
+browser.netscape=Netscape;netscape;<browser> <url>;<browser> -remote openURL(<url>)
+browser.firefox=FireFox;firefox;<browser> <url>;<browser> -new-tab <url>;<browser> -new-window <url>
+browser.mozilla-firefox=FireFox;mozilla-firefox;<browser> <url>;<browser> -remote openURL(<url>);<browser> -new-window <url>
+browser.konqueror=Konqueror;kfmclient;<browser> openURL <url>;<browser> newTab <url>;<browser> openURL <url>
+browser.opera=Opera;opera;<browser> <url>;<browser> -newpage <url>;<browser> -newwindow <url>
+browser.epiphany=Epiphany;epiphany;<browser> <url>;<browser> --new-tab <url>
diff --git a/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties b/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties
new file mode 100644
index 0000000..af32b42
--- /dev/null
+++ b/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties
@@ -0,0 +1,32 @@
+# ************************************************
+#    Copyright 2006 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: sunOSConfig.properties,v 1.2 2006/09/12 15:40:34 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# list of browsers and arguments for using them
+# display name | executable name | start browser args | invoke already started browser
+browser.sdtwebclient=Default;sdtwebclient;<browser> <url>;<browser> -remote openURL(<url>)
+browser.mozilla=Mozilla;mozilla;<browser> <url>;<browser> -remote openURL(<url>)
+browser.netscape=Netscape;netscape;<browser> <url>;<browser> -remote openURL(<url>)
+browser.firefox=FireFox;firefox;<browser> <url>;<browser> -remote openURL(<url>);<browser> -new-window <url>
+browser.opera=Opera;opera;<browser> <url>;<browser> -newpage <url>;<browser> -newwindow <url>
+
diff --git a/edu/stanford/ejalbert/launching/windows/windowsConfig.properties b/edu/stanford/ejalbert/launching/windows/windowsConfig.properties
new file mode 100644
index 0000000..abdcd2e
--- /dev/null
+++ b/edu/stanford/ejalbert/launching/windows/windowsConfig.properties
@@ -0,0 +1,48 @@
+# ************************************************
+#    Copyright 2006,2007 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: windowsConfig.properties,v 1.6 2007/08/30 19:38:09 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# windows versions and arguments for launching a browser
+# command for starting default browser | command for starting a specific browser | use registry
+windows.winVista=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.win2000=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.win9x=command.com /c start "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.winNT=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+
+# properties used to find browsers in program files folder
+program.files.template={0}:\\Program Files
+# drive letters to try when looking for Program Files folder
+drive.letters=C;D;E
+
+#windows.win2000=cmd.exe /c start "" "<url>";cmd.exe /c start <browser> <args> "<url>"
+#windows.win9x=command.com /c start "<url>";command.com /c start <browser> <args> "<url>"
+#windows.winNT=cmd.exe /c start "" "<url>";cmd.exe /c start <browser> <args> "<url>"
+
+# list of browsers and arguments for using them and discovering them
+# browser display name | browser exe name | new window argument | directory containing exe
+browser.mozilla=Mozilla;mozilla;;mozilla.org
+browser.netscape=Netscape;netscape;;Netscape
+browser.firefox=FireFox;firefox;-new-window;Mozilla Firefox
+browser.opera=Opera;opera;-newwindow;Opera
+browser.ie=IE;iexplore;;Internet Explorer
+browser.kmeleon=K-Meleon;k-meleon;;K-Meleon
diff --git a/edu/stanford/ejalbert/resources/Debugging.properties b/edu/stanford/ejalbert/resources/Debugging.properties
new file mode 100644
index 0000000..c80e945
--- /dev/null
+++ b/edu/stanford/ejalbert/resources/Debugging.properties
@@ -0,0 +1,59 @@
+# ************************************************
+#    Copyright 2005 Jeff Chapman
+
+#    This file is part of BrowserLauncher2.
+
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# ************************************************/
+# $Id: Debugging.properties,v 1.8 2007/01/31 18:31:22 jchapman0 Exp $
+
+# Resource file containing strings and other info for use by the Testing
+# tool.
+
+# initial message to display
+debug.mssg=\
+Please let us know the results of testing on your OS/Java configuration. The \
+information displayed below as well as any stack traces will be \
+helpful. Use the copy button to copy and paste the data into an email.
+
+# system properties to query and display, | and ; as separators
+# display|property.name;
+debug.propnames=\
+Java Version: |java.version;\
+Java Vendor: |java.vendor;\
+OS Name: |os.name;\
+OS Version: |os.version
+
+label.url=Enter a url:
+label.app.title=BrowserLauncher2 Test App 1.0
+label.logging.level=Logging Level:
+label.browser.list=Browser List:
+label.window.policy=Force New Window
+
+bttn.browse=Browse
+bttn.copy=Copy
+bttn.set.logging=Set Logging Level
+bttn.set.preference=Set Browser Preference
+
+logging.level.select.title=Logging Level Selection
+logging.level.select.message=Select a logging level from the list below
+
+url.default=file://localhost/
+
+# properties for logging interface
+logging.dateformat=yyyy-MM-dd HH:mm:ss,SSS
+
+logging.level.labels=DEBUG;INFO;WARN;ERROR
diff --git a/source/at/jta/NotSupportedOSException.java b/source/at/jta/NotSupportedOSException.java
new file mode 100644
index 0000000..a82b57b
--- /dev/null
+++ b/source/at/jta/NotSupportedOSException.java
@@ -0,0 +1,28 @@
+// $Id: NotSupportedOSException.java,v 1.1 2007/08/30 19:40:24 jchapman0 Exp $
+package at.jta;
+
+/********************************************************************************************************************************
+ *
+ * <p>Title: Exception is thrown if you use the regor class on not windows machines </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2007</p>
+ *
+ * <p>Company: Taschek Joerg</p>
+ *
+ * @author Taschek Joerg
+ * @version 1.0
+ *******************************************************************************************************************************/
+public class NotSupportedOSException
+    extends RuntimeException
+{
+  /******************************************************************************************************************************
+   * Constructor with message to throw
+   * @param str String
+   *****************************************************************************************************************************/
+  public NotSupportedOSException(String str)
+  {
+    super(str);
+  }
+}
diff --git a/source/at/jta/RegistryErrorException.java b/source/at/jta/RegistryErrorException.java
new file mode 100644
index 0000000..1760c73
--- /dev/null
+++ b/source/at/jta/RegistryErrorException.java
@@ -0,0 +1,31 @@
+// $Id: RegistryErrorException.java,v 1.1 2007/08/30 19:40:24 jchapman0 Exp $
+package at.jta;
+
+import java.io.IOException;
+
+/*******************************************************************************************************************************
+ *
+ * <p>Title: class for throwing exceptions </p>
+ *
+ * <p>Description: </p>
+ *
+ * <p>Copyright: Copyright (c) 2007</p>
+ *
+ * <p>Company: Taschek Joerg</p>
+ *
+ * @author Taschek Joerg
+ * @version 1.0
+ ******************************************************************************************************************************/
+public class RegistryErrorException
+    extends IOException
+{
+
+  /******************************************************************************************************************************
+   * Constructor with message to throw
+   * @param reason String
+   *****************************************************************************************************************************/
+  public RegistryErrorException(String reason)
+  {
+    super(reason);
+  }
+}
diff --git a/source/at/jta/Regor.java b/source/at/jta/Regor.java
new file mode 100644
index 0000000..94e75b4
--- /dev/null
+++ b/source/at/jta/Regor.java
@@ -0,0 +1,741 @@
+// $Id: Regor.java,v 1.1 2007/08/30 19:40:24 jchapman0 Exp $
+package at.jta;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.ArrayList;
+
+/********************************************************************************************************************************
+ *
+ * <p>Title: Class is 100% pur Java Registry handle (just can write/read string values)</p>
+ *
+ * <p>Description:  You can read, delete or create any key in the registry (when you have access rights).
+ * But you just can read/delete and set string values! The java.dll doenst provide any other feature </p>
+ *
+ * <p>Copyright: Copyright (c) 2006</p>
+ *
+ * <p>Company: Taschek Joerg</p>
+ *
+ * @author Taschek Joerg
+ * @version 2.0 22.03.2007 Methods are renamed and now called by the function they are implementing and the document is now in
+ *              english, instead of german
+ *******************************************************************************************************************************/
+final public class Regor
+{
+  /**
+   * the handle to the HKEY_CLASSES_ROOT registry root node
+   */
+  public static final int HKEY_CLASSES_ROOT = 0x80000000;
+  /**
+   * the handle to the HEKY_CURRENT_USER registry root node
+   */
+  public static final int HKEY_CURRENT_USER = 0x80000001;
+  /**
+   * the handle to the HKEY_LOCAL_MACHINE registry root node
+   */
+  public static final int HKEY_LOCAL_MACHINE = 0x80000002;
+
+
+  public static final int ERROR_SUCCESS = 0;
+  public static final int ERROR_FILE_NOT_FOUND = 2;
+  public static final int ERROR_ACCESS_DENIED = 5;
+
+  /* Constants used to interpret returns of native functions    */
+  public static final int NATIVE_HANDLE = 0;
+  public static final int ERROR_CODE = 1;
+  public static final int SUBKEYS_NUMBER = 0;
+  public static final int VALUES_NUMBER = 2;
+  public static final int MAX_KEY_LENGTH = 3;
+  public static final int MAX_VALUE_NAME_LENGTH = 4;
+
+
+  /* Windows security masks */
+  /**
+   * Security Mask need by openKey - just for delete
+   */
+  public static final int DELETE = 0x10000;
+  /**
+   * Security Mask need by openKey - just for querying values
+   */
+  public static final int KEY_QUERY_VALUE = 1;
+  /**
+   * Security Mask need by openKey - just for setting values
+   */
+  public static final int KEY_SET_VALUE = 2;
+  /**
+   * Security Mask need by openKey - for creating sub keys
+   */
+  public static final int KEY_CREATE_SUB_KEY = 4;
+  /**
+   * Security Mask need by openKey - for enum sub keys
+   */
+  public static final int KEY_ENUMERATE_SUB_KEYS = 8;
+  /**
+   * Security Mask need by openKey - for key reading
+   */
+  public static final int KEY_READ = 0x20019;
+  /**
+   * Security Mask need by openKey - for writing keys
+   */
+  public static final int KEY_WRITE = 0x20006;
+  /**
+   * Security Mask need by openKey - highest access to do everything (default access by openkey without security mask)
+   */
+  public static final int KEY_ALL_ACCESS = 0xf003f;
+
+  private Method openKey = null;
+  private Method closeKey = null;
+  private Method delKey = null;
+  private Method createKey = null;
+  private Method flushKey = null;
+  private Method queryValue = null;
+  private Method setValue = null;
+  private Method delValue = null;
+  private Method queryInfoKey = null;
+  private Method enumKey = null;
+  private Method enumValue = null;
+
+  /******************************************************************************************************************************
+   * Constructor to handle with windows registry
+   * @throws RegistryErrorException throws an registryerrorException when its not able to get a handle to the registry methods
+   * @throws NotSupportedOSException throws an notSupportedOSException if the registry is not used in windows
+   *****************************************************************************************************************************/
+  public Regor() throws RegistryErrorException
+  {
+    checkOS();
+    initMethods();
+  }
+
+  /******************************************************************************************************************************
+   * Method checks either if its windows or anohter system (if not windows an exception is thrown) - just needed for internal checks
+   *****************************************************************************************************************************/
+  private void checkOS()
+  {
+    String str = System.getProperty("os.name");
+    if(str == null || str.toLowerCase().indexOf("windows") == -1)
+      throw new NotSupportedOSException("Operating system: " + str + " is not supported!");
+  }
+
+  /******************************************************************************************************************************
+   * Reading every valueName (not only the string value) out of the registry handle (for maximum value index and maxValueNameLength
+   * use the getChildInformation method
+   * @param key the handle to the parent key obtained from openKey
+   * @param valueNameIndex the index of the valueName name - starting from 0 going to the maximum count from the getChildInformation
+   * stored in array index 2
+   * @param maxValueNameLength maximum length of valueName name (used because for memory allocating in the java.dll - if you obtain
+   * the size from getChildInformation increase the [4] int array by 1)
+   * @return byte[] either the name of the valueName or null if not found or an error occurs or if the maxValueNameLength is to short
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public byte[] enumValueName(int key, int valueNameIndex, int maxValueNameLength) throws RegistryErrorException
+  {
+    try
+    {
+      return (byte[])enumValue.invoke(null, new Object[] {new Integer(key), new Integer(valueNameIndex), new Integer(maxValueNameLength)});
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /*****************************************************************************************************************************
+   * Returns every valueName (not only the String value names)
+   * @param key either one of the root nodes or a key obtained from openKey
+   * @param subkey a string to a subkey - if  the subkey is empty or null the information will be obtained from the given key
+   * @return List on success and found a filled list with strings will be returned - on error or nothing found null will be returned
+   * @throws RegistryErrorException
+   ****************************************************************************************************************************/
+  public List listValueNames(int key, String subkey) throws RegistryErrorException
+  {
+    int handle = -1;
+    try{
+      handle = openKey(key, subkey, KEY_READ); //just reading priv
+      if(handle != -1)
+      {
+        int info[] = getChildInformation(handle); //obtain the informations
+        if(info != null && info[0] != -1)
+        {
+          List ret = new ArrayList();
+          for(int x = 0; x != info[2]; x++)
+          {
+            String tmp = parseValue(enumValueName(handle,x,info[4] + 1));
+            if(tmp != null) //just if not null, maybe there are no valueNames
+              ret.add(tmp);
+          }
+          return ret.isEmpty() ? null : ret;
+        }
+      }
+    }
+    catch(RegistryErrorException ex)
+    {
+      throw ex;
+    }
+    catch(Exception ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    finally{
+      closeKey(handle);
+    }
+    return null;
+  }
+
+  /*****************************************************************************************************************************
+   * Returns every valueName (not only the String value names)
+   * @param key either one of the root nodes or a key obtained from openKey
+   * @return List on success and found a filled list with strings will be returned - on error or nothing found null will be returned
+   * @throws RegistryErrorException
+   ****************************************************************************************************************************/
+  public List listValueNames(int key) throws RegistryErrorException
+  {
+    return listValueNames(key,null);
+  }
+
+  /******************************************************************************************************************************
+   * Reading the subkey name out of the registry (to obtain the count and the maxKeyNameLength use <code>getChildInformation</code>
+   * method
+   * @param key the handle to the key obtained by openKey
+   * @param subkeyIndex index from the subkey from which you want to obtain the name (start with 0 - the maximum count you get from
+   * getChildInformation method in array [0])
+   * @param maxKeyNameLength the maximum length of a subkey name (used because for memory allocating in the java.dll - if you obtain
+   * the size from getChildInformation increase the [3] int array by 1 )
+   * @return byte[] on error or not found or the maxKeyNameLength is to short it will returns null, on success the name of the subkey
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public byte[] enumKeys(int key, int subkeyIndex, int maxKeyNameLength) throws RegistryErrorException
+  {
+    try
+    {
+      return (byte[])enumKey.invoke(null, new Object[] {new Integer(key), new Integer(subkeyIndex), new Integer(maxKeyNameLength)});
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Returns all subkeys from the given key and subkey
+   * @param key either one of the root nodes or a key obtained from openKey
+   * @param subkey a string to a subkey - if  the subkey is empty or null the information will be obtained from the given key
+   * @return List on success and found a filled list with strings will be returned - on error or nothing found null will be returned
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public List listKeys(int key, String subkey) throws RegistryErrorException
+  {
+    int handle = -1;
+    try{
+      handle = openKey(key, subkey, KEY_READ); //just reading priv
+      if(handle != -1)
+      {
+        int info[] = getChildInformation(handle); //obtain the informations
+        if(info != null && info[0] != -1)
+        {
+          List ret = new ArrayList();
+          for(int x = 0; x != info[0]; x++)
+          {
+            String tmp = parseValue(enumKeys(handle,x,info[3] + 1));
+            if(tmp != null) //just if not null, maybe there are no valueNames
+              ret.add(tmp);
+          }
+          return ret.isEmpty() ? null : ret;
+        }
+      }
+    }
+    catch(RegistryErrorException ex)
+    {
+      throw ex;
+    }
+    catch(Exception ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    finally{
+      closeKey(handle);
+    }
+    return null;
+  }
+
+  /*****************************************************************************************************************************
+   * Returns all subkeys from the given key
+   * @param key either one of the root nodes or a key obtained from openKey
+   * @return List on success and found a filled list with strings will be returned - on error or nothing found null will be returned
+   * @throws RegistryErrorException
+   ****************************************************************************************************************************/
+  public List listKeys(int key) throws RegistryErrorException
+  {
+    return listKeys(key,null);
+  }
+
+
+  /******************************************************************************************************************************
+   * Reads information about the current opened key (use it when you want to enumKey or enumValueName to determine the maximum
+   * key length and the count of keys)
+   * @param key the key which you obtained from openKey
+   * @return int[0] the count of the subkeys,[2] count of valuenames,
+   * [3] the maximum length of a subkey! the maximum length of valuename is stored in[4]
+   * (for other operations you should increase the [3] or [4] value by 1 because of the terminating \0 in C - because you handle
+   * with the java.dll)
+   * if nothing found or illegal key, the values are -1 of the array (at index 1 the value would be 6 the other -1)
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int[] getChildInformation(int key) throws RegistryErrorException
+  {
+    try
+    {
+      return (int[])queryInfoKey.invoke(null, new Object[] {new Integer(key)});
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Method deletes the specified String value
+   * @param key the key obtained by openKey
+   * @param valueName name of String value you want to delete (if the string is empty or null the default entry will be
+   * deleted)
+   * @return int
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int delValue(int key, String valueName) throws RegistryErrorException
+  {
+    try
+    {
+      Integer ret = (Integer)delValue.invoke(null, new Object[] {new Integer(key), getString(valueName)});
+      if(ret != null)
+        return ret.intValue();
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Method set the specified string value
+   * Methode setzt (oder erstellt) einen Wert auf eine Zeichenfolge
+   * Will man den defaulteintrag �ndern, so muss man valueName "" �bergeben
+   * @param key obtained by openKey
+   * @param valueName the string value name in the registry you want to set
+   * @param value the new value you want to set
+   * @return on success, return is ERROR_SUCCESS if not -1 or sth else will be returned
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int setValue(int key,String valueName, String value) throws RegistryErrorException
+  {
+    try
+    {
+      Integer ret = (Integer)setValue.invoke(null, new Object[] {new Integer(key), getString(valueName), getString(value)});
+      if(ret != null)
+        return ret.intValue();
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Reads the value of an string value
+   * @param key obtained from openKey
+   * @param valueName the string value which you want to read (if you want to obtain the default entry the valueName should be
+   * empty or NULL)
+   * @return byte[] if found the data in the string value will be returned (to get a string use the class method parseValue(byte[]))
+   * on error NULL will be returned
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public byte[] readValue(int key, String valueName) throws RegistryErrorException
+  {
+    try
+    {
+      byte ret[] = (byte[])queryValue.invoke(null, new Object[] {new Integer(key), getString(valueName)});
+      return ret;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Flush method - dont know what the method exactly does just implemented because i found it in the java sun source
+   * @param key obtained the key from openKey
+   * @return on success, ERROR_SUCESS will be returned! on error -1 or sth else
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int flushKey(int key) throws RegistryErrorException
+  {
+    try
+    {
+      Integer ret = (Integer)flushKey.invoke(null, new Object[] {new Integer(key)});
+      if(ret != null)
+        return ret.intValue();
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * deletes a key/subkey from the registry
+   * @param key the parent key obtained by openKey
+   * @param subkey the key name you want to delete
+   * @return int ERROR_SUCCESS wenn erfolgreich
+   * @throws RegistryErrorException if subkey is empty or null or any other exception occurs
+   *****************************************************************************************************************************/
+  public int delKey(int key, String subkey) throws RegistryErrorException
+  {
+    if(subkey == null || subkey.length() == 0)
+      throw new RegistryErrorException("subkey cannot be null");
+    try
+    {
+      Integer ret = (Integer)delKey.invoke(null, new Object[] {new Integer(key), getString(subkey)});
+      if(ret != null)
+        return ret.intValue();
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Create new key/subkey in the registry with the specified name
+   * Attentition: if the key is successfully returned, you should close and open the key again, because the obtained key
+   * doesnt have a high access level (so maybe creating or deleting a key/value wouldn�t be successful)
+   * @param key handle to parent key obtained from openKey
+   * @param subkey name of the key/subkey you want to create
+   * @return on success the handle to the new key will be returned, otherwhise it will be -1
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int createKey(int key, String subkey) throws RegistryErrorException
+  {
+    try
+    {
+      int result[] = (int[])createKey.invoke(null, new Object[] {new Integer(key), getString(subkey)});
+      if(result[ERROR_CODE] == ERROR_SUCCESS)
+        return result[NATIVE_HANDLE];
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+
+
+  /*****************************************************************************************************************************
+   * Close an obtained key for right usage
+   * @param key the key handle
+   * @return int on error it will be -1
+   * @throws RegistryErrorException
+   ****************************************************************************************************************************/
+  public int closeKey(int key) throws RegistryErrorException
+  {
+    try
+    {
+      Integer ret = (Integer)closeKey.invoke(null, new Object[] {new Integer(key)});
+      if(ret != null)
+        return ret.intValue();
+      else
+        return -1;
+    }
+    catch (InvocationTargetException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalArgumentException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+    catch (IllegalAccessException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Opens a registry key
+   * @param key one of the registry root nodes - either HKEY_CLASSES_ROOT, HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE
+   * @param subkey the name of the key/subkey like SOFTWARE or HARDWARE - for subkeys use the \\ as delimiter f.e. : SOFTWARE\\MICROSOFT
+   * if subkey name is "" or null it returns the handle to the root node
+   * @param security_mask the security mask to handle with the opened key (see security mask doc at the begin for detailed information)
+   * @return int on error -1 (when not found or not allowed) otherwhise the handle to the obtained key
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int openKey(int key, String subkey, int security_mask) throws RegistryErrorException
+  {
+    try
+    {
+      int[] result = (int[])openKey.invoke(null, new Object[]{new Integer(key),getString(subkey),new Integer(security_mask)});
+      if(result == null || result[ERROR_CODE] != ERROR_SUCCESS)
+        return -1;
+      else
+        return result[NATIVE_HANDLE];
+    }
+    catch (InvocationTargetException ex1)
+    {
+      throw new RegistryErrorException(ex1.getMessage());
+    }
+    catch (IllegalArgumentException ex1)
+    {
+      throw new RegistryErrorException(ex1.getMessage());
+    }
+    catch (IllegalAccessException ex1)
+    {
+      throw new RegistryErrorException(ex1.getMessage());
+    }
+  }
+
+  /******************************************************************************************************************************
+   * Opens a registry key
+   * @param key one of the registry root nodes - either HKEY_CLASSES_ROOT, HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE
+   * @param subkey the name of the key/subkey like SOFTWARE or HARDWARE - for subkeys use the \\ as delimiter f.e. : SOFTWARE\\MICROSOFT
+   * if subkey name is "" or null it returns the handle to the root node
+   * @return int -1 if not found or not allowed (attention here this methods allways uses the KEY_ALL_ACCESS security mask)
+   * on success the handle to key will be returned
+   * @throws RegistryErrorException
+   *****************************************************************************************************************************/
+  public int openKey(int key, String subkey) throws RegistryErrorException
+  {
+    return openKey(key,subkey,KEY_ALL_ACCESS);
+  }
+
+  /******************************************************************************************************************************
+   * Intern method which adds the trailing \0 for the handle with java.dll
+   * @param str String
+   * @return byte[]
+   *****************************************************************************************************************************/
+  private byte[] getString(String str)
+  {
+    if(str == null)
+      str = "";
+    return (str += "\0").getBytes();
+  }
+
+  /******************************************************************************************************************************
+   * Method removes the trailing \0 which is returned from the java.dll (just if the last sign is a \0)
+   * @param buf the byte[] buffer which every read method returns
+   * @return String a parsed string without the trailing \0
+   *****************************************************************************************************************************/
+  public static String parseValue(byte buf[])
+  {
+    if(buf == null)
+      return null;
+    String ret = new String(buf);
+    if(ret.charAt(ret.length() - 1) == '\0')
+      return ret.substring(0,ret.length() - 1);
+    return ret;
+  }
+
+
+  /******************************************************************************************************************************
+   * intern method which obtain the methods via reflection from the java.util.prefs.WindowPreferences (tested with java 1.4, 1.5
+   * and java 1.6)
+   * @throws RegistryErrorException exception is thrown if any method is not found or if the class is not found
+   *****************************************************************************************************************************/
+  private void initMethods() throws RegistryErrorException
+  {
+    Class clazz = null;
+    try
+    {
+      clazz = Class.forName("java.util.prefs.WindowsPreferences"); //da der Zugriff anders nicht erlaubt wird
+
+      Method ms[] = clazz.getDeclaredMethods();
+      if(ms == null)
+        throw new RegistryErrorException("Cannot access java.util.prefs.WindowsPreferences class!");
+      //geht die Methoden durch und speichert diese in den Variablen ab
+      for(int x = 0; x != ms.length; x++)
+      {
+        if(ms[x] != null)
+        {
+          if(ms[x].getName().equals("WindowsRegOpenKey"))
+          {
+            openKey = ms[x];
+            openKey.setAccessible(true); //set Access for private
+          }
+          else if(ms[x].getName().equals("WindowsRegCloseKey"))
+          {
+            closeKey = ms[x];
+            closeKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegCreateKeyEx"))
+          {
+            createKey = ms[x];
+            createKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegDeleteKey"))
+          {
+            delKey = ms[x];
+            delKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegFlushKey"))
+          {
+            flushKey = ms[x];
+            flushKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegQueryValueEx"))
+          {
+            queryValue = ms[x];
+            queryValue.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegSetValueEx"))
+          {
+            setValue = ms[x];
+            setValue.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegDeleteValue"))
+          {
+            delValue = ms[x];
+            delValue.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegQueryInfoKey"))
+          {
+            queryInfoKey = ms[x];
+            queryInfoKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegEnumKeyEx"))
+          {
+            enumKey = ms[x];
+            enumKey.setAccessible(true);
+          }
+          else if(ms[x].getName().equals("WindowsRegEnumValue"))
+          {
+            enumValue = ms[x];
+            enumValue.setAccessible(true);
+          }
+        }
+      }
+    }
+    catch (ClassNotFoundException ex)
+    {
+      throw new RegistryErrorException(ex.getMessage());
+    }
+  }
+
+
+  /******************************************************************************************************************************
+   * main for testing and some examples are stored here
+   * @param args String[]
+   * @throws Exception
+   *****************************************************************************************************************************/
+  public static void main(String[] args) throws Exception
+  {
+    Regor regor = new Regor();
+    //opening dhe LOCAL_MACHINE entry and software\microsoft - the delimiter is the \\
+    int key = regor.openKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft"), key2 = -1;
+    //listing the subkeys
+    List l = regor.listKeys(key);
+    System.out.println("SOME KEYS....");
+    for(int x = 0; l != null && x != l.size(); x++) //printing out the keys
+      System.out.println(x + " == " + l.get(x));
+    if(l.size() > 0) //if keys found, use first key to get valueNames
+      key2 = regor.openKey(key,(String)l.get(0));
+    l = regor.listValueNames(key2); //read the valueNames
+    System.out.println("SOME VALUENAMES.....");
+    for(int x = 0; l != null && x != l.size(); x++) //printing it
+      System.out.println(x + " == " + l.get(x));
+    System.out.println("SOME STRING VALUES....");
+    for(int x = 0; l != null && x != l.size(); x++) //getting the String value from the valueNames
+    {
+      byte buf[] = regor.readValue(key2,(String)l.get(x)); //get the information - if is not a string value, null will be returned
+      System.out.println(x + ": " + l.get(x) + " == " + Regor.parseValue(buf)); //parses the byte buffer to String
+    }
+    //example to access the default valueName - either null or ""
+    System.out.println("default entry == " + Regor.parseValue(regor.readValue(key,null)));
+    //accessing a root node
+    l = regor.listKeys(HKEY_LOCAL_MACHINE);
+    System.out.println("KEYS FROM LOCAL_MACHINE....");
+    for(int x = 0; l != null && x != l.size(); x++) //printing out the keys
+      System.out.println(x + " == " + l.get(x));
+    regor.closeKey(key2);
+    regor.closeKey(key);
+  }
+}
diff --git a/source/edu/stanford/ejalbert/BrowserLauncher.java b/source/edu/stanford/ejalbert/BrowserLauncher.java
new file mode 100644
index 0000000..d7f63ad
--- /dev/null
+++ b/source/edu/stanford/ejalbert/BrowserLauncher.java
@@ -0,0 +1,407 @@
+/************************************************
+    Copyright 2004,2005,2006,2007 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLauncher.java,v 1.13 2007/08/27 14:12:51 jchapman0 Exp $
+package edu.stanford.ejalbert;
+
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.exceptionhandler.
+        BrowserLauncherDefaultErrorHandler;
+import edu.stanford.ejalbert.exceptionhandler.BrowserLauncherErrorHandler;
+import edu.stanford.ejalbert.launching.BrowserLaunchingFactory;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+import net.sf.wraplog.NoneLogger;
+
+/**
+ * BrowserLauncher provides an API to open browsers (default or
+ * targetted) from within a Java application.
+ * <p>
+ * Primary API methods:
+ * <p>
+ * {@link #openURLinBrowser(String, String) openURLinBrowser(browser, url)}
+ * Opens the url in the requested browser.
+ * <p>
+ * {@link #openURLinBrowser(String) openURLinBrowser(url)}
+ * Opens the url in the default browser.
+ * <p>
+ * {@link #getBrowserList getBrowserList()}
+ * Returns the list of browsers that are available for browser
+ * targetting.
+ * <p>
+ * The following protocols have been tested: http, mailto, and file.
+ * <p>
+ * This library is written completely in Java and will run on
+ * all JDK 4.x-compliant systems without modification or a need
+ * for additional libraries.
+ * <p>
+ * There are certain system requirements for this library, as
+ * it's running through Runtime.exec(), which is Java's way of
+ * making a native system call. Currently, Macintosh requires a
+ * Finder which supports the GURL event, which is true for Mac OS
+ * 8.0 and 8.1 systems that have the Internet Scripting
+ * AppleScript dictionary installed in the Scripting Additions
+ * folder in the Extensions folder (which is installed by default,
+ * as far as the library authors know, under Mac OS 8.0 and 8.1),
+ * and for all Mac OS 8.5 and later systems. On Windows, it only
+ * runs under Win32 systems (Windows 9x and NT 4.0, as well as
+ * later versions).
+ * <p>
+ * The browserlauncher2 project is based on the original
+ * browserlauncher project. It has been significantly altered
+ * and extended.</br/>
+ * <b>This is the original copyright notice and credits from the
+ * browserlauncher project:</b><br/>
+ * This code is Copyright 1999-2001 by Eric Albert (ejalbert at cs.stanford.edu) and may be
+ * redistributed or modified in any form without restrictions as long as the portion of this
+ * comment from this paragraph through the end of the comment is not removed.  The author
+ * requests that he be notified of any application, applet, or other binary that makes use of
+ * this code, but that's more out of curiosity than anything and is not required.  This software
+ * includes no warranty.  The author is not repsonsible for any loss of data or functionality
+ * or any adverse or unexpected effects of using this software.
+ * <p>
+ * Credits:
+ * <br>Steven Spencer, JavaWorld magazine (<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip 66</a>)
+ * <br>Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea Cantatore,
+ * Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
+ * <br/><b>End of the original copyright and credits.</b>
+ * @author Eric Albert
+ * @author Markus Gebhard
+ * @author Jeff Chapman
+ */
+public class BrowserLauncher {
+    /**
+     * Key to system property containing name of users
+     * preferred browser.
+     * <p>
+     * The value is defined in IBrowserLaunching but exposed for
+     * general API usage from the BrowserLauncher class.
+     */
+    public static final String BROWSER_SYSTEM_PROPERTY =
+            IBrowserLaunching.BROWSER_SYSTEM_PROPERTY;
+    /**
+     * Key to system property that controls how browsers are discovered
+     * when running on a Windows O/S. The values are registry and disk.
+     * <p>
+     * The value is defined in IBrowserLaunching but exposed for
+     * general API usage from the BrowserLauncher class.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_PROPERTY =
+            IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_PROPERTY;
+    /**
+     * Value associated with WINDOWS_BROWSER_DISC_POLICY_PROPERTY.
+     * <p>
+     * The value is defined in IBrowserLaunching but exposed for
+     * general API usage from the BrowserLauncher class.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_DISK =
+            IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_DISK;
+    /**
+     * Value associated with WINDOWS_BROWSER_DISC_POLICY_PROPERTY.
+     * <p>
+     * The value is defined in IBrowserLaunching but exposed for
+     * general API usage from the BrowserLauncher class.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_REGISTRY =
+            IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_REGISTRY;
+
+    private final IBrowserLaunching launching; // in ctor
+    private AbstractLogger logger; // in init method
+    private BrowserLauncherErrorHandler errorHandler; // in ctor
+
+    /**
+     * Initializes the browser launcher for the operating system on which
+     * the application is running.
+     * <p>
+     * This method will use the default logger
+     * {@link net.sf.wraplog.NoneLogger NoneLogger}. All log messages are
+     * ignored by this logger.
+     * <p>
+     * This method will use the default errorHandler
+     * {@link edu.stanford.ejalbert.exceptionhandler.BrowserLauncherDefaultErrorHandler BrowserLauncherDefaultErrorHandler}.
+     * It will print a stack trace to the console. The errorHandler is used
+     * to catch and handle exceptions when executing the browser
+     * launch in a separate thread.
+     *
+     * @throws BrowserLaunchingInitializingException
+     * @throws UnsupportedOperatingSystemException
+     */
+    public BrowserLauncher()
+            throws BrowserLaunchingInitializingException,
+            UnsupportedOperatingSystemException {
+        this(null, null);
+    }
+
+    /**
+     * Initializes the browser launcher for the operating system on which
+     * the application is running.
+     * <p>
+     * If null is passed in as a logger, the default logger used will
+     * be {@link net.sf.wraplog.NoneLogger NoneLogger}. All log messages are
+     * ignored by this logger.
+     * <p>
+     * This method will use the default errorHandler
+     * {@link edu.stanford.ejalbert.exceptionhandler.BrowserLauncherDefaultErrorHandler BrowserLauncherDefaultErrorHandler}.
+     * It will print a stack trace to the console. The errorHandler is used
+     * to catch and handle exceptions when executing the browser
+     * launch in a separate thread.
+
+     * @param logger AbstractLogger
+     * @throws BrowserLaunchingInitializingException
+     * @throws UnsupportedOperatingSystemException
+     */
+    public BrowserLauncher(AbstractLogger logger)
+            throws BrowserLaunchingInitializingException,
+            UnsupportedOperatingSystemException {
+        this(logger, null);
+    }
+
+    /**
+     * Initializes the browser launcher for the operating system on which
+     * the application is running.
+     * <p>
+     * If null is passed in as a logger, the default logger used will
+     * be {@link net.sf.wraplog.NoneLogger NoneLogger}. All log messages are
+     * ignored by this logger.
+     * <p>
+     * If null is passed for the errorHandler, the default errorHandler
+     * used will be
+     * {@link edu.stanford.ejalbert.exceptionhandler.BrowserLauncherDefaultErrorHandler BrowserLauncherDefaultErrorHandler}.
+     * It will print a stack trace to the console. The errorHandler is used
+     * to catch and handle exceptions when executing the browser
+     * launch in a separate thread.
+     *
+     * @param logger AbstractLogger
+     * @param errorHandler BrowserLauncherErrorHandler
+     * @throws BrowserLaunchingInitializingException
+     * @throws UnsupportedOperatingSystemException
+     */
+    public BrowserLauncher(AbstractLogger logger,
+                           BrowserLauncherErrorHandler errorHandler)
+            throws BrowserLaunchingInitializingException,
+            UnsupportedOperatingSystemException {
+        // assign logger or use default
+        if (logger == null) {
+            logger = new NoneLogger();
+        }
+        this.logger = logger;
+        // assign error handler or use default
+        if (errorHandler == null) {
+            errorHandler = new BrowserLauncherDefaultErrorHandler();
+        }
+        this.errorHandler = errorHandler;
+        // init and assign IBrowserLaunching instance
+        // this method assumes the logger is not null
+        this.launching = initBrowserLauncher();
+    }
+
+    /**
+     * Determines the operating system and loads the necessary runtime data.
+     * <p>
+     * If null is passed in as a logger, the default logger used will
+     * be {@link net.sf.wraplog.NoneLogger NoneLogger}. All log messages are
+     * ignored by this logger.
+     *
+     * @param logger AbstractLogger
+     * @return IBrowserLaunching
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingInitializingException
+     */
+    private IBrowserLaunching initBrowserLauncher()
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingInitializingException {
+        if (logger == null) {
+            throw new IllegalArgumentException(
+                    "the logger cannot be null at this point.");
+        }
+        IBrowserLaunching launching =
+                BrowserLaunchingFactory.createSystemBrowserLaunching(logger);
+        launching.initialize();
+        return launching;
+    }
+
+    /**
+     * Attempts to open the default web browser to the given URL.
+     * @deprecated -- create a BrowserLauncher object and use it instead of
+     *                calling this static method.
+     * @param urlString The URL to open
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public static void openURL(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        BrowserLauncher launcher = new BrowserLauncher(null);
+        launcher.openURLinBrowser(urlString);
+    }
+
+    /**
+     * Opens a browser and url from the command line. Useful for testing.
+     * The first argument is the url to be opened. All other arguments will
+     * be ignored.
+     *
+     * @param args String[]
+     */
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            System.err.println("Usage: java -jar BrowserLauncher.jar url_value");
+        }
+        else {
+            try {
+                BrowserLauncher launcher = new BrowserLauncher(null);
+                launcher.openURLinBrowser(args[0]);
+            }
+            catch (BrowserLaunchingInitializingException ex) {
+                ex.printStackTrace();
+            }
+            //catch (BrowserLaunchingExecutionException ex) {
+            //    ex.printStackTrace();
+            // }
+            catch (UnsupportedOperatingSystemException ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+
+    /* ---------------------- API Methods -------------------- */
+
+    /**
+     * Returns the logger being used by this BrowserLauncher instance.
+     *
+     * @return AbstractLogger
+     */
+    public AbstractLogger getLogger() {
+        return logger;
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item:
+     * {@link edu.stanford.ejalbert.launching.IBrowserLaunching#BROWSER_DEFAULT BROWSER_DEFAULT}.
+     * @see IBrowserLaunching
+     * @return List
+     */
+    public List getBrowserList() {
+        return launching.getBrowserList();
+    }
+
+    /**
+     * Attempts to open a browser and direct it to the passed url.
+     *
+     * @todo what to do if the url is null or empty?
+     * @param urlString String
+     */
+    public void openURLinBrowser(String urlString) {
+        Runnable runner = new BrowserLauncherRunner(
+                launching,
+                urlString,
+                logger,
+                errorHandler);
+        Thread launcherThread = new Thread(runner);
+        launcherThread.start();
+    }
+
+    /**
+     * Attempts to open a specific browser and direct it to the passed url. If
+     * the call to the requested browser fails, the code will fail over to the
+     * default browser.
+     * <p>
+     * The name for the targetted browser should come from the list
+     * returned from {@link #getBrowserList() getBrowserList}.
+     *
+     * @param browser String
+     * @param urlString String
+     */
+    public void openURLinBrowser(String browser,
+                                 String urlString) {
+        Runnable runner = new BrowserLauncherRunner(
+                launching,
+                browser,
+                urlString,
+                logger,
+                errorHandler);
+        Thread launcherThread = new Thread(runner);
+        launcherThread.start();
+    }
+
+    /**
+     * Iterates through the list of browsers until it finds one
+     * that is available on the user's system. The method then
+     * opens the browser and directs it to the passed url. This
+     * method allows the caller to try multiple browsers in a
+     * specific order.
+     * <p>
+     * If the list is null or empty or none of the browsers in
+     * the list are available, the code will fail over to the
+     * default browser method
+     * {@link #openURLinBrowser(String) openURLinBrowser(url)}.
+     * <p>
+     * The name for the targetted browsers should come from the
+     * list returned from
+     * {@link #getBrowserList() getBrowserList}.
+     *
+     * @param browsers List
+     * @param urlString String
+     */
+    public void openURLinBrowser(List browsers,
+                                 String urlString) {
+        Runnable runner = new BrowserLauncherRunner(
+                launching,
+                browsers,
+                urlString,
+                logger,
+                errorHandler);
+        Thread launcherThread = new Thread(runner);
+        launcherThread.start();
+    }
+
+    /**
+     * Returns the policy used for opening a url in a browser.
+     * <p>
+     * If the policy is true, an attempt will be made to force the
+     * url to be opened in a new instance (window) of the
+     * browser.
+     * <p>
+     * If the policy is false, the url may open in a new window or
+     * a new tab.
+     * <p>
+     * Results will vary based on the O/S and browser being targetted.
+     *
+     * @return boolean
+     */
+    public boolean getNewWindowPolicy() {
+        return launching.getNewWindowPolicy();
+    }
+
+    /**
+     * Sets the policy used for opening a url in a browser.
+     *
+     * @param forceNewWindow boolean
+     */
+    public void setNewWindowPolicy(boolean forceNewWindow) {
+        launching.setNewWindowPolicy(forceNewWindow);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/BrowserLauncherRunner.java b/source/edu/stanford/ejalbert/BrowserLauncherRunner.java
new file mode 100644
index 0000000..113f5ef
--- /dev/null
+++ b/source/edu/stanford/ejalbert/BrowserLauncherRunner.java
@@ -0,0 +1,144 @@
+/************************************************
+    Copyright 2005,2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLauncherRunner.java,v 1.6 2006/04/11 13:36:48 jchapman0 Exp $
+package edu.stanford.ejalbert;
+
+import edu.stanford.ejalbert.exceptionhandler.BrowserLauncherErrorHandler;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+import java.util.List;
+
+/**
+ * This is a convenience class to facilitate executing the browser launch in
+ * a separate thread. This class is used from within BrowserLauncher
+ * when handling calls to open a url.
+ *
+ * @author Jeff Chapman
+ */
+class BrowserLauncherRunner
+        implements Runnable {
+    private final List targetBrowsers; // in ctor
+    private final String targetBrowser; // in ctor
+    private final String url; // in ctor
+    private final BrowserLauncherErrorHandler errorHandler; // in ctor
+    private final IBrowserLaunching launcher; // in ctor
+    private final AbstractLogger logger; // in ctor
+
+    /**
+     * Takes the items necessary for launching a browser and handling any
+     * exceptions.
+     *
+     * @param launcher IBrowserLaunching
+     * @param url String
+     * @param logger AbstractLogger
+     * @param errorHandler BrowserLauncherErrorHandler
+     */
+    BrowserLauncherRunner(IBrowserLaunching launcher,
+                          String url,
+                          AbstractLogger logger,
+                          BrowserLauncherErrorHandler errorHandler) {
+        this(launcher, null, null, url, logger, errorHandler);
+    }
+
+    BrowserLauncherRunner(IBrowserLaunching launcher,
+                          String browserName,
+                          String url,
+                          AbstractLogger logger,
+                          BrowserLauncherErrorHandler errorHandler) {
+        this(launcher, browserName, null, url, logger, errorHandler);
+    }
+
+    BrowserLauncherRunner(IBrowserLaunching launcher,
+                          List browserList,
+                          String url,
+                          AbstractLogger logger,
+                          BrowserLauncherErrorHandler errorHandler) {
+        this(launcher, null, browserList, url, logger, errorHandler);
+    }
+
+    /**
+     * Takes the items necessary for launching a browser and handling any
+     * exceptions.
+     *
+     * @param launcher IBrowserLaunching
+     * @param browserName String
+     * @param url String
+     * @param logger AbstractLogger
+     * @param errorHandler BrowserLauncherErrorHandler
+     */
+    private BrowserLauncherRunner(IBrowserLaunching launcher,
+                                  String browserName,
+                                  List browserList,
+                                  String url,
+                                  AbstractLogger logger,
+                                  BrowserLauncherErrorHandler errorHandler) {
+        if (launcher == null) {
+            throw new IllegalArgumentException("launcher cannot be null.");
+        }
+        if (url == null) {
+            throw new IllegalArgumentException("url cannot be null.");
+        }
+        if (errorHandler == null) {
+            throw new IllegalArgumentException("errorHandler cannot be null.");
+        }
+        if (logger == null) {
+            throw new IllegalArgumentException("logger cannot be null");
+        }
+        this.targetBrowsers = browserList;
+        this.launcher = launcher;
+        this.url = url;
+        this.targetBrowser = browserName;
+        this.errorHandler = errorHandler;
+        this.logger = logger;
+    }
+
+    /* ------------------- from Runnable -------------------- */
+
+    /**
+     * When an object implementing interface <code>Runnable</code> is used to
+     * create a thread, starting the thread causes the object's
+     * <code>run</code> method to be called in that separately executing
+     * thread.
+     * <p>
+     * This method will make the call to open the browser and display the
+     * url. If an exception occurs, it will be passed to the instance of
+     * BrowserLauncherErrorHandler that has been passed into the constructor.
+     */
+    public void run() {
+        try {
+            if (targetBrowser != null) {
+                launcher.openUrl(targetBrowser,
+                                 url);
+            }
+            else if(targetBrowsers != null) {
+                launcher.openUrl(targetBrowsers,
+                                 url);
+            }
+            else {
+                launcher.openUrl(url);
+            }
+        }
+        catch (Exception ex) {
+            logger.error("fatal error opening url", ex);
+            errorHandler.handleException(ex);
+        }
+    }
+}
diff --git a/source/edu/stanford/ejalbert/browserprefui/BrowserPrefAction.java b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefAction.java
new file mode 100644
index 0000000..c2d6c18
--- /dev/null
+++ b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefAction.java
@@ -0,0 +1,104 @@
+/************************************************
+    Copyright 2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserPrefAction.java,v 1.1 2006/09/26 19:40:28 jchapman0 Exp $
+package edu.stanford.ejalbert.browserprefui;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Icon;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+import edu.stanford.ejalbert.BrowserLauncher;
+
+/**
+ *
+ * @author Jeff Chapman
+ * @version 1.0
+ */
+public class BrowserPrefAction
+        extends AbstractAction {
+    private final BrowserLauncher browserLauncher; // in ctor
+    private final JFrame appFrame; // in ctor
+
+    public BrowserPrefAction(String name,
+                             BrowserLauncher browserLauncher,
+                             JFrame appFrame) {
+        super(name);
+        if(browserLauncher == null) {
+            throw new IllegalArgumentException("browserLauncher cannot be null");
+        }
+        this.browserLauncher = browserLauncher;
+        this.appFrame = appFrame;
+    }
+
+    public BrowserPrefAction(String name,
+                             Icon icon,
+                             BrowserLauncher browserLauncher,
+                             JFrame appFrame) {
+        super(name, icon);
+        if(browserLauncher == null) {
+            throw new IllegalArgumentException("browserLauncher cannot be null");
+        }
+        this.browserLauncher = browserLauncher;
+        this.appFrame = appFrame;
+    }
+
+    /* --------------------------- from Action --------------------------- */
+
+    /**
+     * Launches a browser preferences dialog and sets the system
+     * property BrowserLauncher.BROWSER_SYSTEM_PROPERTY with
+     * the requested browser.
+     * <p>
+     * Browser prefs dialog will be placed in the Swing thread queue
+     * to enable action performed method to return immediately.
+     *
+     * @param e ActionEvent
+     */
+    public void actionPerformed(ActionEvent e) {
+        final ActionEvent event = e;
+        Runnable runner = new Runnable() {
+            public void run() {
+                try {
+                    BrowserPrefDialog dlg = new BrowserPrefDialog(
+                            appFrame,
+                            browserLauncher);
+                    dlg.setLocationRelativeTo(appFrame);
+                    dlg.pack();
+                    dlg.setSize(275,200);
+                    dlg.setVisible(true);
+                    String prefBrowser = dlg.getSelectedBrowser();
+                    if(prefBrowser != null) {
+                        System.setProperty(
+                                BrowserLauncher.BROWSER_SYSTEM_PROPERTY,
+                                prefBrowser);
+                    }
+                }
+                catch (Exception ex) {
+                    browserLauncher.getLogger().error("problem getting/setting browser pref", ex);
+                }
+            }
+        };
+        SwingUtilities.invokeLater(runner);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/browserprefui/BrowserPrefDialog.java b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefDialog.java
new file mode 100644
index 0000000..eddc2e6
--- /dev/null
+++ b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefDialog.java
@@ -0,0 +1,130 @@
+/************************************************
+    Copyright 2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserPrefDialog.java,v 1.1 2006/09/26 19:40:28 jchapman0 Exp $
+package edu.stanford.ejalbert.browserprefui;
+
+import java.awt.BorderLayout;
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.HeadlessException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+
+import edu.stanford.ejalbert.BrowserLauncher;
+
+/**
+ *
+ * @author Jeff Chapman
+ * @version 1.0
+ */
+public class BrowserPrefDialog
+        extends JDialog {
+    private JList browserList = new JList();
+    private String selectedBrowser = null;
+    private static final String UI_BUNDLE =
+            "edu.stanford.ejalbert.browserprefui.BrowserPrefs";
+
+    public BrowserPrefDialog(Dialog owner,
+                             BrowserLauncher launcher)
+            throws HeadlessException {
+        super(owner, true);
+        initDialog(launcher);
+    }
+
+    public BrowserPrefDialog(Frame owner,
+                             BrowserLauncher launcher)
+            throws HeadlessException {
+        super(owner, true);
+        initDialog(launcher);
+    }
+
+    public String getSelectedBrowser() {
+        return selectedBrowser;
+    }
+
+    private void initDialog(BrowserLauncher launcher)
+            throws MissingResourceException {
+        this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+        // get resource bundle
+        ResourceBundle rbundle = ResourceBundle.getBundle(UI_BUNDLE);
+        // set title
+        this.setTitle(rbundle.getString("dialog.title"));
+        // init list of browsers
+        List browsers = launcher.getBrowserList();
+        browserList.setListData(browsers.toArray());
+        browserList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        // get selected browser from system prop
+        String prefBrowser = System.getProperty(
+            BrowserLauncher.BROWSER_SYSTEM_PROPERTY,
+            null);
+        if(prefBrowser != null) {
+            browserList.setSelectedValue(prefBrowser, true);
+        }
+        initGui(rbundle);
+    }
+
+    private void okButtonClicked() {
+        selectedBrowser = (String)browserList.getSelectedValue();
+        dispose();
+    }
+
+    private void cancelButtonClicked() {
+        dispose();
+    }
+
+    private void initGui(ResourceBundle rbundle)
+            throws MissingResourceException {
+        JButton okButton = new JButton(rbundle.getString("dialog.bttn.ok"));
+        JButton cancelButton = new JButton(rbundle.getString("dialog.bttn.cancel"));
+        JScrollPane browserListScroll = new JScrollPane(browserList);
+        // init ok button
+        okButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent ae) {
+                okButtonClicked();
+            }
+        });
+        // init cancel button
+        cancelButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent ae) {
+                cancelButtonClicked();
+            }
+        });
+        // create panels
+        JPanel mainPanel = new JPanel(new BorderLayout(0,2));
+        JPanel buttonsPanel = new JPanel();
+        // format controls
+        buttonsPanel.add(okButton);
+        buttonsPanel.add(cancelButton);
+        mainPanel.add(browserListScroll, BorderLayout.CENTER);
+        mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
+        this.getContentPane().add(mainPanel);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties
new file mode 100644
index 0000000..8a9bf91
--- /dev/null
+++ b/source/edu/stanford/ejalbert/browserprefui/BrowserPrefs.properties
@@ -0,0 +1,26 @@
+# ************************************************
+#    Copyright 2006 Jeff Chapman
+
+#    This file is part of BrowserLauncher2.
+
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# ************************************************/
+# $Id: BrowserPrefs.properties,v 1.1 2006/09/26 19:40:41 jchapman0 Exp $
+
+dialog.title=Browser Preference
+
+dialog.bttn.ok=OK
+dialog.bttn.cancel=Cancel
diff --git a/source/edu/stanford/ejalbert/exception/BrowserLaunchingExecutionException.java b/source/edu/stanford/ejalbert/exception/BrowserLaunchingExecutionException.java
new file mode 100644
index 0000000..6c12d7f
--- /dev/null
+++ b/source/edu/stanford/ejalbert/exception/BrowserLaunchingExecutionException.java
@@ -0,0 +1,40 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLaunchingExecutionException.java,v 1.2 2006/03/23 20:30:50 jchapman0 Exp $
+package edu.stanford.ejalbert.exception;
+
+/**
+ * Exception thrown if there is problem during the attempt to launch
+ * a browser.
+ *
+ * @author Markus Gebhard
+ */
+public class BrowserLaunchingExecutionException
+        extends Exception {
+
+    public BrowserLaunchingExecutionException(Throwable cause) {
+        super(cause);
+    }
+
+    public BrowserLaunchingExecutionException(String message) {
+        super(message);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/exception/BrowserLaunchingInitializingException.java b/source/edu/stanford/ejalbert/exception/BrowserLaunchingInitializingException.java
new file mode 100644
index 0000000..e1f73fa
--- /dev/null
+++ b/source/edu/stanford/ejalbert/exception/BrowserLaunchingInitializingException.java
@@ -0,0 +1,39 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLaunchingInitializingException.java,v 1.2 2006/03/23 20:30:50 jchapman0 Exp $
+package edu.stanford.ejalbert.exception;
+
+/**
+ * Exception thrown if there is a problem during initialization.
+ *
+ * @author Markus Gebhard
+ */
+public class BrowserLaunchingInitializingException
+        extends Exception {
+
+    public BrowserLaunchingInitializingException(Exception cause) {
+        super(cause);
+    }
+
+    public BrowserLaunchingInitializingException(String message) {
+        super(message);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/exception/UnsupportedOperatingSystemException.java b/source/edu/stanford/ejalbert/exception/UnsupportedOperatingSystemException.java
new file mode 100644
index 0000000..71e9308
--- /dev/null
+++ b/source/edu/stanford/ejalbert/exception/UnsupportedOperatingSystemException.java
@@ -0,0 +1,40 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: UnsupportedOperatingSystemException.java,v 1.2 2006/03/23 20:30:50 jchapman0 Exp $
+package edu.stanford.ejalbert.exception;
+
+/**
+ * Exception thrown when the Operating System is not supported by
+ * the browser launcher project.
+ *
+ * @author Markus Gebhard
+ */
+public class UnsupportedOperatingSystemException
+        extends Exception {
+
+    public UnsupportedOperatingSystemException(String message) {
+        super(message);
+    }
+
+    public UnsupportedOperatingSystemException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherDefaultErrorHandler.java b/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherDefaultErrorHandler.java
new file mode 100644
index 0000000..212b45b
--- /dev/null
+++ b/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherDefaultErrorHandler.java
@@ -0,0 +1,44 @@
+/************************************************
+    Copyright 2005 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLauncherDefaultErrorHandler.java,v 1.1 2005/02/03 00:58:23 jchapman0 Exp $
+package edu.stanford.ejalbert.exceptionhandler;
+
+/**
+ * The default implementation for BrowserLauncherErrorHandler. It simply
+ * prints a stack trace to the console.
+ *
+ * @author Jeff Chapman
+ */
+public class BrowserLauncherDefaultErrorHandler
+        implements BrowserLauncherErrorHandler {
+
+    public BrowserLauncherDefaultErrorHandler() {
+    }
+
+    /**
+     * Handles exception by printing a stack trace to the console.
+     *
+     * @param ex Exception
+     */
+    public void handleException(Exception ex) {
+        ex.printStackTrace();
+    }
+}
diff --git a/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherErrorHandler.java b/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherErrorHandler.java
new file mode 100644
index 0000000..5a419c9
--- /dev/null
+++ b/source/edu/stanford/ejalbert/exceptionhandler/BrowserLauncherErrorHandler.java
@@ -0,0 +1,41 @@
+/************************************************
+    Copyright 2005 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLauncherErrorHandler.java,v 1.1 2005/02/03 00:58:23 jchapman0 Exp $
+package edu.stanford.ejalbert.exceptionhandler;
+
+/**
+ * This is an interface to be used by the BrowserLauncherRunner for handling
+ * exceptions. Applications should implement this interface to handle
+ * exceptions in an application specific manner.
+ *
+ * @author Jeff Chapman
+ */
+public interface BrowserLauncherErrorHandler {
+
+    /**
+     * Takes an exception and does something with it. Usually the implementing
+     * class will want to log the exception or display some information about
+     * it to the user.
+     *
+     * @param ex Exception
+     */
+    public void handleException(Exception ex);
+}
diff --git a/source/edu/stanford/ejalbert/launching/BrowserDescription.java b/source/edu/stanford/ejalbert/launching/BrowserDescription.java
new file mode 100644
index 0000000..04a63ad
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/BrowserDescription.java
@@ -0,0 +1,45 @@
+/************************************************
+    Copyright 2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserDescription.java,v 1.1 2006/03/23 20:27:29 jchapman0 Exp $
+package edu.stanford.ejalbert.launching;
+
+/**
+ * Interface to general information about a browser.
+ *
+ * @author Jeff Chapman
+ * @version 1.0
+ */
+public interface BrowserDescription {
+
+    /**
+     * Returns browser display name.
+     *
+     * @returns String
+     */
+    public String getBrowserDisplayName();
+
+    /**
+     * Returns name of executable.
+     *
+     * @returns String
+     */
+    public String getBrowserApplicationName();
+}
diff --git a/source/edu/stanford/ejalbert/launching/BrowserLaunchingFactory.java b/source/edu/stanford/ejalbert/launching/BrowserLaunchingFactory.java
new file mode 100644
index 0000000..6d6ec21
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/BrowserLaunchingFactory.java
@@ -0,0 +1,127 @@
+/************************************************
+    Copyright 2004,2005,2006,2007 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLaunchingFactory.java,v 1.7 2007/08/27 14:14:10 jchapman0 Exp $
+package edu.stanford.ejalbert.launching;
+
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.macos.MacOs2_0BrowserLaunching;
+import edu.stanford.ejalbert.launching.macos.MacOs2_1BrowserLaunching;
+import edu.stanford.ejalbert.launching.macos.MacOs3_0BrowserLaunching;
+import edu.stanford.ejalbert.launching.macos.MacOs3_1BrowserLaunching;
+import edu.stanford.ejalbert.launching.misc.SunOSBrowserLaunching;
+import edu.stanford.ejalbert.launching.misc.UnixNetscapeBrowserLaunching;
+import edu.stanford.ejalbert.launching.windows.WindowsBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+
+/**
+ * Factory for determining the OS and returning the appropriate version
+ * of IBrowserLaunching. The factory uses
+ * {@link System#getProperty(String) System.getProperty("os.name")} to
+ * determine the OS.
+ *
+ * @author Markus Gebhard
+ */
+public class BrowserLaunchingFactory {
+
+    /**
+     * Analyzes the name of the underlying operating system
+     * based on the "os.name" system property and returns
+     * the IBrowserLaunching version appropriate for the
+     * O/S.
+     *
+     * @param logger AbstractLogger
+     * @return IBrowserLaunching
+     * @throws UnsupportedOperatingSystemException
+     */
+    public static IBrowserLaunching createSystemBrowserLaunching(
+            AbstractLogger logger)
+            throws UnsupportedOperatingSystemException {
+        String osName = System.getProperty("os.name");
+        if (osName.startsWith("Mac OS")) {
+            logger.info("Mac OS");
+            String mrjVersion = System.getProperty("mrj.version");
+            String majorMRJVersion = mrjVersion.substring(0, 3);
+            try {
+                double version = Double.valueOf(majorMRJVersion).doubleValue();
+                logger.info("version=" + Double.toString(version));
+                if (version == 2) {
+                    return new MacOs2_0BrowserLaunching();
+                }
+                else if (version >= 2.1 && version < 3) {
+                    // Assume that all 2.x versions of MRJ work the same.  MRJ 2.1 actually
+                    // works via Runtime.exec() and 2.2 supports that but has an openURL() method
+                    // as well that we currently ignore.
+                    return new MacOs2_1BrowserLaunching();
+                }
+                else if (version == 3.0) {
+                    return new MacOs3_0BrowserLaunching();
+                }
+                else if (version >= 3.1) {
+                    // Assume that all 3.1 and later versions of MRJ work the same.
+                    return new MacOs3_1BrowserLaunching();
+                }
+                else {
+                    throw new UnsupportedOperatingSystemException(
+                            "Unsupported MRJ version: " + version);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                throw new UnsupportedOperatingSystemException(
+                        "Invalid MRJ version: " + mrjVersion);
+            }
+        }
+        else if (osName.startsWith("Windows")) {
+            logger.info("Windows OS");
+            if (osName.indexOf("9") != -1 ||
+                osName.indexOf("Windows Me") != -1) {
+                return new WindowsBrowserLaunching(
+                        logger,
+                        WindowsBrowserLaunching.WINKEY_WIN9X);
+            }
+            else if(osName.indexOf("Vista") != -1) {
+                return new WindowsBrowserLaunching(
+                        logger,
+                        WindowsBrowserLaunching.WINKEY_WINVISTA);
+            }
+            else if (osName.indexOf("2000") != -1 ||
+                     osName.indexOf("XP") != -1) {
+                return new WindowsBrowserLaunching(
+                        logger,
+                        WindowsBrowserLaunching.WINKEY_WIN2000);
+            }
+            else {
+                return new WindowsBrowserLaunching(
+                        logger,
+                        WindowsBrowserLaunching.WINKEY_WINNT);
+            }
+        }
+        else if (osName.startsWith("SunOS")) {
+            logger.info("SunOS");
+            return new SunOSBrowserLaunching(logger);
+        }
+        else {
+            logger.info("Unix-type OS");
+            return new UnixNetscapeBrowserLaunching(
+                    logger,
+                    UnixNetscapeBrowserLaunching.CONFIGFILE_LINUX_UNIX);
+        }
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/IBrowserLaunching.java b/source/edu/stanford/ejalbert/launching/IBrowserLaunching.java
new file mode 100644
index 0000000..27aab48
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/IBrowserLaunching.java
@@ -0,0 +1,177 @@
+/************************************************
+    Copyright 2004,2005,2006,2007 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: IBrowserLaunching.java,v 1.9 2007/08/27 14:15:20 jchapman0 Exp $
+package edu.stanford.ejalbert.launching;
+
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+
+/**
+ * Main interface to the Browser Launching methods.
+ *
+ * @author Markus Gebhard
+ */
+public interface IBrowserLaunching {
+    /**
+     * Key to system property containing name of users
+     * preferred browser.
+     */
+    public static final String BROWSER_SYSTEM_PROPERTY =
+            "edu.stanford.ejalbert.preferred.browser";
+    /**
+     * Key to system property that controls how browsers are discovered
+     * when running on a Windows O/S.
+     * <p>
+     * The values are registry and disk.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_PROPERTY =
+            "win.browser.disc.policy";
+    /**
+     * Value associated with WINDOWS_BROWSER_DISC_POLICY_PROPERTY.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_DISK = "disk";
+    /**
+     * Value associated with WINDOWS_BROWSER_DISC_POLICY_PROPERTY.
+     */
+    public static final String WINDOWS_BROWSER_DISC_POLICY_REGISTRY = "registry";
+    /**
+     * property file key for delimiter character used in other properties.
+     */
+    public static final String PROP_KEY_DELIMITER = "delimchar";
+    /**
+     * prefix used for property file keys that define a browser
+     */
+    public static final String PROP_KEY_BROWSER_PREFIX = "browser.";
+    /**
+     * http protocol
+     */
+    public static final String PROTOCOL_HTTP = "http";
+    /**
+     * file protocol
+     */
+    public static final String PROTOCOL_FILE = "file";
+    /**
+     * mailto protocol
+     */
+    public static final String PROTOCOL_MAILTO = "mailto";
+    /**
+     * Identifier for the system's default browser.
+     */
+    public static final String BROWSER_DEFAULT = "Default";
+
+    /**
+     * Performs any initialization needed for the particular O/S.
+     *
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void initialize()
+            throws BrowserLaunchingInitializingException;
+
+    /**
+     * Opens the passed url in the system's default browser.
+     *
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException;
+
+    /**
+     * Allows user to target a specific browser. The names of
+     * potential browsers can be accessed via the
+     * {@link #getBrowserList() getBrowserList} method.
+     * <p>
+     * If the call to the requested browser fails, the code will
+     * fail over to the default browser.
+     *
+     * @param browser String
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String browser,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException;
+
+    /**
+     * Allows user to target several browsers. The names of
+     * potential browsers can be accessed via the
+     * {@link #getBrowserList() getBrowserList} method.
+     * <p>
+     * The browsers from the list will be tried in order
+     * (first to last) until one of the calls succeeds. If
+     * all the calls to the requested browsers fail, the code
+     * will fail over to the default browser.
+     *
+     * @param browsers List
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(List browsers,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException;
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item:
+     * {@link #BROWSER_DEFAULT BROWSER_DEFAULT}.
+     *
+     * @return List
+     */
+    public List getBrowserList();
+
+    /**
+     * Returns the policy used for opening a url in a browser.
+     * <p>
+     * If the policy is true, an attempt will be made to force the
+     * url to be opened in a new instance (window) of the
+     * browser.
+     * <p>
+     * If the policy is false, the url may open in a new window or
+     * a new tab.
+     * <p>
+     * Results will vary based on the O/S and browser being targetted.
+     *
+     * @return boolean
+     */
+    public boolean getNewWindowPolicy();
+
+    /**
+     * Sets the policy used for opening a url in a browser.
+     *
+     * @param forceNewWindow boolean
+     */
+    public void setNewWindowPolicy(boolean forceNewWindow);
+}
diff --git a/source/edu/stanford/ejalbert/launching/macos/MacOs2_0BrowserLaunching.java b/source/edu/stanford/ejalbert/launching/macos/MacOs2_0BrowserLaunching.java
new file mode 100644
index 0000000..e77950d
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/macos/MacOs2_0BrowserLaunching.java
@@ -0,0 +1,153 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: MacOs2_0BrowserLaunching.java,v 1.3 2006/04/11 13:36:48 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.macos;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+
+/**
+ * @author Markus Gebhard
+ */
+public class MacOs2_0BrowserLaunching
+        extends MacOsBrowserLaunching {
+
+    /** The name for the AppleEvent type corresponding to a GetURL event. */
+    private static final String GURL_EVENT = "GURL";
+
+    private Class aeDescClass;
+    private Constructor aeTargetConstructor;
+    private Constructor appleEventConstructor;
+    private Constructor aeDescConstructor;
+    private Method makeOSType;
+    private Method putParameter;
+    private Method sendNoReply;
+    private Integer keyDirectObject;
+    private Integer kAutoGenerateReturnID;
+    private Integer kAnyTransactionID;
+
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        try {
+            Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
+            Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
+            Class appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
+            Class aeClass = Class.forName("com.apple.MacOS.ae");
+            aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
+
+            aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new
+                    Class[] {int.class});
+            appleEventConstructor =
+                    appleEventClass.getDeclaredConstructor(
+                            new Class[] {int.class, int.class, aeTargetClass, int.class, int.class});
+            aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class[] {
+                    String.class});
+
+            makeOSType = osUtilsClass.getDeclaredMethod("makeOSType",
+                    new Class[] {String.class});
+            putParameter = appleEventClass.getDeclaredMethod("putParameter",
+                    new Class[] {int.class, aeDescClass});
+            sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply",
+                    new Class[] {
+            });
+
+            Field keyDirectObjectField = aeClass.getDeclaredField(
+                    "keyDirectObject");
+            keyDirectObject = (Integer) keyDirectObjectField.get(null);
+            Field autoGenerateReturnIDField = appleEventClass.getDeclaredField(
+                    "kAutoGenerateReturnID");
+            kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField.get(null);
+            Field anyTransactionIDField = appleEventClass.getDeclaredField(
+                    "kAnyTransactionID");
+            kAnyTransactionID = (Integer) anyTransactionIDField.get(null);
+        }
+        catch (Exception cnfe) {
+            throw new BrowserLaunchingInitializingException(cnfe);
+        }
+    }
+
+    private Object getBrowser()
+            throws BrowserLaunchingInitializingException {
+        try {
+            Integer finderCreatorCode = (Integer) makeOSType.invoke(null,
+                    new Object[] {FINDER_CREATOR});
+            Object aeTarget = aeTargetConstructor.newInstance(new Object[] {
+                    finderCreatorCode});
+            Integer gurlType = (Integer) makeOSType.invoke(null,
+                    new Object[] {GURL_EVENT});
+            Object appleEvent =
+                    appleEventConstructor.newInstance(
+                            new Object[] {gurlType, gurlType, aeTarget,
+                            kAutoGenerateReturnID, kAnyTransactionID});
+            // Don't set browser = appleEvent because then the next time we call
+            // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
+            // added the relevant parameter. Instead, regenerate the AppleEvent every time.
+            // There's probably a way to do this better; if any has any ideas, please let
+            // me know.
+            return appleEvent;
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingInitializingException(e);
+        }
+    }
+
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        Object browser = getBrowser();
+        Object aeDesc = null;
+        try {
+            aeDesc = aeDescConstructor.newInstance(new Object[] {urlString});
+            putParameter.invoke(browser, new Object[] {keyDirectObject, aeDesc});
+            sendNoReply.invoke(browser, new Object[] {
+            });
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+        finally {
+            //TODO Oct 10, 2003 (Markus Gebhard): Unnecessary, because local variables - isn't it?
+            aeDesc = null; // Encourage it to get disposed if it was created
+            browser = null; // Ditto
+        }
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        List browserList = new ArrayList(1);
+        browserList.add(IBrowserLaunching.BROWSER_DEFAULT);
+        return browserList;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/macos/MacOs2_1BrowserLaunching.java b/source/edu/stanford/ejalbert/launching/macos/MacOs2_1BrowserLaunching.java
new file mode 100644
index 0000000..82e80db
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/macos/MacOs2_1BrowserLaunching.java
@@ -0,0 +1,144 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: MacOs2_1BrowserLaunching.java,v 1.3 2006/04/11 13:36:48 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.macos;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+
+/**
+ * @author Markus Gebhard
+ */
+public class MacOs2_1BrowserLaunching
+        extends MacOsBrowserLaunching implements IBrowserLaunching {
+
+    /**
+     * The file type of the Finder on a Macintosh.  Hardcoding "Finder" would keep non-U.S. English
+     * systems from working properly.
+     */
+    private static final String FINDER_TYPE = "FNDR";
+
+    private Object kSystemFolderType;
+    private Method findFolder;
+    private Method getFileCreator;
+    private Method getFileType;
+
+    private String browser;
+
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        try {
+            Class mrjFileUtilsClass = Class.forName(
+                    "com.apple.mrj.MRJFileUtils");
+            Class mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
+            Field systemFolderField = mrjFileUtilsClass.getDeclaredField(
+                    "kSystemFolderType");
+            kSystemFolderType = systemFolderField.get(null);
+            findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder",
+                    new Class[] {mrjOSTypeClass});
+            getFileCreator = mrjFileUtilsClass.getDeclaredMethod(
+                    "getFileCreator", new Class[] {File.class});
+            getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType",
+                    new Class[] {File.class});
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingInitializingException(e);
+        }
+    }
+
+    private String getBrowser()
+            throws BrowserLaunchingInitializingException {
+        if (browser != null) {
+            return browser;
+        }
+
+        File systemFolder;
+        try {
+            systemFolder = (File) findFolder.invoke(null,
+                    new Object[] {kSystemFolderType});
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingInitializingException(e);
+        }
+        String[] systemFolderFiles = systemFolder.list();
+        // Avoid a FilenameFilter because that can't be stopped mid-list
+        for (int i = 0; i < systemFolderFiles.length; i++) {
+            try {
+                File file = new File(systemFolder, systemFolderFiles[i]);
+                if (!file.isFile()) {
+                    continue;
+                }
+                // We're looking for a file with a creator code of 'MACS' and
+                // a type of 'FNDR'.  Only requiring the type results in non-Finder
+                // applications being picked up on certain Mac OS 9 systems,
+                // especially German ones, and sending a GURL event to those
+                // applications results in a logout under Multiple Users.
+                Object fileType = getFileType.invoke(null, new Object[] {file});
+                if (FINDER_TYPE.equals(fileType.toString())) {
+                    Object fileCreator = getFileCreator.invoke(null,
+                            new Object[] {file});
+                    if (FINDER_CREATOR.equals(fileCreator.toString())) {
+                        browser = file.toString(); // Actually the Finder, but that's OK
+                        return browser;
+                    }
+                }
+            }
+            catch (Exception e) {
+                throw new BrowserLaunchingInitializingException(e);
+            }
+        }
+        throw new BrowserLaunchingInitializingException("Unable to find finder");
+    }
+
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        String browser = getBrowser();
+        try {
+            Runtime.getRuntime().exec(new String[] {browser, urlString});
+        }
+        catch (IOException e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        List browserList = new ArrayList(1);
+        browserList.add(IBrowserLaunching.BROWSER_DEFAULT);
+        return browserList;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/macos/MacOs3_0BrowserLaunching.java b/source/edu/stanford/ejalbert/launching/macos/MacOs3_0BrowserLaunching.java
new file mode 100644
index 0000000..ab1a339
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/macos/MacOs3_0BrowserLaunching.java
@@ -0,0 +1,110 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: MacOs3_0BrowserLaunching.java,v 1.3 2006/04/11 13:36:48 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.macos;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.stanford.ejalbert.BrowserLauncher;
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+
+/**
+ * @author Markus Gebhard
+ */
+public class MacOs3_0BrowserLaunching
+        extends MacOsBrowserLaunching {
+
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        //TODO Oct 10, 2003 (Markus Gebhard): Can anyone explain what this code is for??
+        try {
+            Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
+            Constructor constructor = linker.getConstructor(new Class[] {Class.class});
+            Object linkage = constructor.newInstance(new Object[] {
+                    BrowserLauncher.class});
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingInitializingException(e);
+        }
+    }
+
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        int[] instance = new int[1];
+        int result = ICStart(instance, 0);
+        if (result == 0) {
+            int[] selectionStart = new int[] {0};
+            byte[] urlBytes = urlString.getBytes();
+            int[] selectionEnd = new int[] {urlBytes.length};
+            result =
+                    ICLaunchURL(instance[0], new byte[] {0}, urlBytes,
+                                urlBytes.length, selectionStart, selectionEnd);
+            if (result == 0) {
+                // Ignore the return value; the URL was launched successfully
+                // regardless of what happens here.
+                ICStop(instance);
+            }
+            else {
+                throw new BrowserLaunchingExecutionException(
+                        "Unable to launch URL: " + result);
+            }
+        }
+        else {
+            throw new BrowserLaunchingExecutionException(
+                    "Unable to create an Internet Config instance: " + result);
+        }
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        List browserList = new ArrayList(1);
+        browserList.add(IBrowserLaunching.BROWSER_DEFAULT);
+        return browserList;
+    }
+
+    /**
+     * Methods required for Mac OS X.  The presence of native methods does not cause
+     * any problems on other platforms.
+     */
+    private native static int ICStart(int[] instance, int signature);
+
+    private native static int ICStop(int[] instance);
+
+    private native static int ICLaunchURL(
+            int instance,
+            byte[] hint,
+            byte[] data,
+            int len,
+            int[] selectionStart,
+            int[] selectionEnd);
+}
diff --git a/source/edu/stanford/ejalbert/launching/macos/MacOs3_1BrowserLaunching.java b/source/edu/stanford/ejalbert/launching/macos/MacOs3_1BrowserLaunching.java
new file mode 100644
index 0000000..bcb5573
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/macos/MacOs3_1BrowserLaunching.java
@@ -0,0 +1,76 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: MacOs3_1BrowserLaunching.java,v 1.3 2006/04/11 13:36:48 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.macos;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+
+/**
+ * @author Markus Gebhard
+ */
+public class MacOs3_1BrowserLaunching
+        extends MacOsBrowserLaunching {
+    private Method openURL;
+
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        try {
+            Class mrjFileUtilsClass = Class.forName(
+                    "com.apple.mrj.MRJFileUtils");
+            openURL = mrjFileUtilsClass.getDeclaredMethod("openURL",
+                    new Class[] {String.class});
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingInitializingException(e);
+        }
+    }
+
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        try {
+            openURL.invoke(null, new Object[] {urlString});
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        List browserList = new ArrayList(1);
+        browserList.add(IBrowserLaunching.BROWSER_DEFAULT);
+        return browserList;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/macos/MacOsBrowserLaunching.java b/source/edu/stanford/ejalbert/launching/macos/MacOsBrowserLaunching.java
new file mode 100644
index 0000000..f3c2c59
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/macos/MacOsBrowserLaunching.java
@@ -0,0 +1,113 @@
+/************************************************
+    Copyright 2004,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: MacOsBrowserLaunching.java,v 1.3 2006/09/11 20:38:13 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.macos;
+
+import java.util.List;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+
+/**
+ * @author Markus Gebhard
+ */
+public abstract class MacOsBrowserLaunching
+        implements IBrowserLaunching {
+    /**
+     * new window policy to apply when opening a url. If true,
+     * try to force url into a new browser instance/window.
+     */
+    private boolean forceNewWindow = false;
+
+    /**
+     * The creator code of the Finder on a Macintosh, which is needed to send AppleEvents to the
+     * application.
+     */
+    protected static final String FINDER_CREATOR = "MACS";
+
+    /* ---------------- from IBrowserLaunching ---------------- */
+
+    /**
+     * Falls through to non-targetted openUrl method. Browser
+     * targetting has not been implemented for the Mac.
+     *
+     * @param browser String
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String browser,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        openUrl(urlString);
+    }
+
+    /**
+     * Falls through to non-targetted openUrl method. Browser
+     * targetting has not been implemented for the Mac.
+     *
+     * @param browsers List
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(List browsers,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        openUrl(urlString);
+    }
+
+    /**
+     * Returns the policy used for opening a url in a browser.
+     * <p>
+     * If the policy is true, an attempt will be made to force the
+     * url to be opened in a new instance (window) of the
+     * browser.
+     * <p>
+     * If the policy is false, the url may open in a new window or
+     * a new tab.
+     * <p>
+     * This is not supported on the Mac OS.
+     *
+     * @return boolean
+     */
+    public boolean getNewWindowPolicy() {
+        return forceNewWindow;
+    }
+
+    /**
+     * Sets the policy used for opening a url in a browser.
+     * This is not supported on the Mac OS.
+     *
+     * @param forceNewWindow boolean
+     */
+    public void setNewWindowPolicy(boolean forceNewWindow) {
+        this.forceNewWindow = forceNewWindow;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/misc/StandardUnixBrowser.java b/source/edu/stanford/ejalbert/launching/misc/StandardUnixBrowser.java
new file mode 100644
index 0000000..cfe7aa8
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/StandardUnixBrowser.java
@@ -0,0 +1,202 @@
+/************************************************
+    Copyright 2004,2005,2006,2007 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: StandardUnixBrowser.java,v 1.8 2007/08/31 15:54:10 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.misc;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import net.sf.wraplog.AbstractLogger;
+import edu.stanford.ejalbert.launching.utils.LaunchingUtils;
+
+/**
+ * Contains information on a unix browser.
+ */
+class StandardUnixBrowser
+        implements UnixBrowser {
+    /**
+     * name of browser for user display
+     */
+    private final String browserName; // in ctor
+    /**
+     * name of browser executable used to invoke it
+     */
+    private final String browserArgName; // in ctor
+    /**
+     * arguments used to address an already open browser.
+     */
+    private final String argsForOpenBrowser; // in ctor
+    /**
+     * arguments used for starting a new instance of a browser.
+     */
+    private final String argsForStartBrowser; // in ctor
+
+    private final String argsForForcedBrowserWindow; // in ctor
+
+    /**
+     * Splits the config string using the configSep character.
+     * The resulting config items are used to set the
+     * browser display name, the browser executable name, and
+     * the arguments for starting a new browser instance and
+     * addressing an already open browser.
+     *
+     * @param configSep String
+     * @param configStr String
+     */
+    StandardUnixBrowser(String configSep,
+                        String configStr) {
+        String[] configItems = configStr.split(configSep, -2);
+        this.browserName = configItems[0];
+        this.browserArgName = configItems[1];
+        this.argsForStartBrowser = configItems[2];
+        this.argsForOpenBrowser = configItems[3];
+        if(configItems.length == 5) {
+            this.argsForForcedBrowserWindow = configItems[4];
+        }
+        else {
+            this.argsForForcedBrowserWindow = configItems[2];
+        }
+    }
+
+    /**
+     * Returns debug information.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("display name=");
+        buf.append(browserName);
+        buf.append(" executable name=");
+        buf.append(browserArgName);
+        buf.append(" argsForStartBrowser=");
+        buf.append(argsForStartBrowser);
+        buf.append(" argsForOpenBrowser=");
+        buf.append(argsForOpenBrowser);
+        return buf.toString();
+    }
+
+    /**
+     * Replaces the placeholders <browser> and <url> in the
+     * argsString and splits the resulting string around
+     * space characters.
+     *
+     * @param argsString String
+     * @param urlString String
+     * @return String[]
+     */
+    private String[] getCommandLineArgs(String argsString,
+                                        String urlString) {
+        argsString = LaunchingUtils.replaceArgs(argsString,
+                                                browserArgName,
+                                                urlString);
+        return argsString.split("[ ]");
+    }
+
+    /* --------------------- from BrowserDescription --------------------- */
+
+    /**
+     * Returns the display name for the browser.
+     *
+     * @return String
+     */
+    public String getBrowserDisplayName() {
+        return browserName;
+    }
+
+    /**
+     * Returns the executable name for the browser.
+     *
+     * @return String
+     */
+    public String getBrowserApplicationName() {
+        return browserArgName;
+    }
+
+    /* ------------------------- from UnixBrowser ------------------------ */
+
+    /**
+     * Returns the command line arguments for addressing an already
+     * open browser.
+     *
+     * @param urlString String
+     * @return String[]
+     */
+    public String[] getArgsForOpenBrowser(String urlString) {
+        String argsStartString = argsForOpenBrowser != null &&
+                                 argsForOpenBrowser.length() > 0 ?
+                                 argsForOpenBrowser : argsForStartBrowser;
+        return getCommandLineArgs(argsStartString, urlString);
+    }
+
+    /**
+     * Returns the command line arguments for starting a new browser
+     * instance.
+     *
+     * @param urlString String
+     * @return String[]
+     */
+    public String[] getArgsForStartingBrowser(String urlString) {
+        return getCommandLineArgs(argsForStartBrowser, urlString);
+    }
+
+    public String[] getArgsForForcingNewBrowserWindow(String urlString) {
+        return getCommandLineArgs(argsForForcedBrowserWindow, urlString);
+    }
+
+    /**
+     * Returns true if the browser is available, ie which command finds it.
+     *
+     * @param logger AbstractLogger
+     * @return boolean
+     */
+    public boolean isBrowserAvailable(AbstractLogger logger) {
+        boolean isAvailable = false;
+        try {
+            Process process = Runtime.getRuntime().exec(new String[] {"which",
+                    browserArgName});
+            InputStream errStream = process.getErrorStream();
+            InputStream inStream = process.getInputStream();
+            BufferedReader errIn
+                    = new BufferedReader(new InputStreamReader(errStream));
+            BufferedReader in
+                    = new BufferedReader(new InputStreamReader(inStream));
+            String whichOutput = in.readLine();
+            String whichErrOutput = errIn.readLine();
+            in.close();
+            errIn.close();
+            if(whichOutput != null) {
+                logger.debug(whichOutput);
+            }
+            if(whichErrOutput != null) {
+                logger.debug(whichErrOutput);
+            }
+            isAvailable = whichOutput != null &&
+                          whichOutput.startsWith("/");
+        }
+        catch (IOException ex) {
+            logger.error("io error executing which command", ex);
+        }
+        return isAvailable;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/misc/SunOSBrowserLaunching.java b/source/edu/stanford/ejalbert/launching/misc/SunOSBrowserLaunching.java
new file mode 100644
index 0000000..57ae413
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/SunOSBrowserLaunching.java
@@ -0,0 +1,98 @@
+/************************************************
+    Copyright 2005,2006 Olivier Hochreutiner, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: SunOSBrowserLaunching.java,v 1.3 2006/10/26 20:02:18 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.misc;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+
+/**
+ * Launches a default browser on SunOS Unix systems using the sdtwebclient
+ * command.
+ *
+ * @author Olivier Hochreutiner
+ */
+public class SunOSBrowserLaunching
+        extends UnixNetscapeBrowserLaunching {
+    /**
+     * config file for SunOS.
+     */
+    public static final String CONFIGFILE_SUNOS =
+            "/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties";
+
+    /**
+     * Passes the logger and config file for SunOS to its
+     * super class.
+     *
+     * @param logger AbstractLogger
+     */
+    public SunOSBrowserLaunching(AbstractLogger logger) {
+        super(logger, CONFIGFILE_SUNOS);
+    }
+
+    /**
+     * Opens a url using the default browser. It uses sdtwebclient
+     * to launch the default browser. The sdtwebclient executable
+     * is mapped to
+     * {@link IBrowserLaunching.BROWSER_DEFAULT IBrowserLaunching.BROWSER_DEFAULT}.
+     *
+     * @param urlString String
+     * @throws BrowserLaunchingExecutionException
+     */
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        try {
+            logger.info(urlString);
+            // check system property which may contain user's preferred browser
+            String browserId = System.getProperty(
+                    IBrowserLaunching.BROWSER_SYSTEM_PROPERTY,
+                    null);
+            StandardUnixBrowser defBrowser = getBrowser(
+                    IBrowserLaunching.BROWSER_DEFAULT);
+            if (browserId != null) {
+                logger.info(
+                        "browser pref defined in system prop. Failing over to super.openUrl() method");
+                super.openUrl(urlString);
+            }
+            // we should always have a default browser defined for
+            // SunOS but if not, fail over to super class method
+            else if (defBrowser == null) {
+                logger.info(
+                        "no default browser defined. Failing over to super.openUrl() method");
+                super.openUrl(urlString);
+            }
+            else {
+                logger.info(defBrowser.getBrowserDisplayName());
+                Process process = Runtime.getRuntime().exec(
+                        defBrowser.getArgsForStartingBrowser(urlString));
+                process.waitFor();
+            }
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/misc/UnixBrowser.java b/source/edu/stanford/ejalbert/launching/misc/UnixBrowser.java
new file mode 100644
index 0000000..b8f520f
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/UnixBrowser.java
@@ -0,0 +1,61 @@
+/************************************************
+    Copyright 2004,2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: UnixBrowser.java,v 1.4 2006/09/11 20:41:55 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.misc;
+
+import edu.stanford.ejalbert.launching.BrowserDescription;
+import net.sf.wraplog.AbstractLogger;
+
+/**
+ * Augments the standard browser description with information
+ * specific to a Unix type browser.
+ */
+public interface UnixBrowser
+        extends BrowserDescription {
+    /**
+     * Returns the command line arguments for addressing an already
+     * open browser.
+     *
+     * @param urlString String
+     * @return String[]
+     */
+    public String[] getArgsForOpenBrowser(String url);
+
+    /**
+     * Returns the command line arguments for starting a new browser
+     * instance.
+     *
+     * @param urlString String
+     * @return String[]
+     */
+    public String[] getArgsForStartingBrowser(String url);
+
+    public String[] getArgsForForcingNewBrowserWindow(String url);
+
+
+    /**
+     * Returns true if the browser is available on the user's system..
+     *
+     * @param logger AbstractLogger
+     * @return boolean
+     */
+    public boolean isBrowserAvailable(AbstractLogger logger);
+}
diff --git a/source/edu/stanford/ejalbert/launching/misc/UnixNetscapeBrowserLaunching.java b/source/edu/stanford/ejalbert/launching/misc/UnixNetscapeBrowserLaunching.java
new file mode 100644
index 0000000..5a899ef
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/UnixNetscapeBrowserLaunching.java
@@ -0,0 +1,375 @@
+/************************************************
+    Copyright 2004,2005,2006 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: UnixNetscapeBrowserLaunching.java,v 1.14 2006/11/07 14:08:31 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.misc;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+
+/**
+ * Tries several browsers (mozilla, netscape, firefox, opera, and konqueror).
+ * Most users will have at least one of these installed. The types are
+ * defined in /edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties.
+ *
+ * @author Markus Gebhard, Jeff Chapman
+ */
+public class UnixNetscapeBrowserLaunching
+        implements IBrowserLaunching {
+    /**
+     * config file for linux/unix
+     */
+    public static final String CONFIGFILE_LINUX_UNIX =
+            "/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties";
+    /**
+     * map of supported unix/linux browsers. The map contains
+     * displayName => StandardUnixBrowser mappings.
+     */
+    private Map unixBrowsers = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+
+    protected final AbstractLogger logger; // in ctor
+    /**
+     * name of config file passed into constructor
+     */
+    private final String configFileName; // in ctor
+    /**
+     * new window policy to apply when opening a url. If true,
+     * try to force url into a new browser instance/window.
+     */
+    private boolean forceNewWindow = false;
+
+    /**
+     * Sets the logger and config file name.
+     *
+     * @param logger AbstractLogger
+     * @param configFile String
+     */
+    public UnixNetscapeBrowserLaunching(AbstractLogger logger,
+                                        String configFile) {
+        if (configFile == null) {
+            throw new IllegalArgumentException("config file cannot be null");
+        }
+        this.logger = logger;
+        this.configFileName = configFile;
+    }
+
+    /**
+     * Provides access the browsers map for extending classes.
+     *
+     * @param key String
+     * @return StandardUnixBrowser
+     */
+    protected StandardUnixBrowser getBrowser(String key) {
+        return (StandardUnixBrowser) unixBrowsers.get(key);
+    }
+
+    /**
+     * Attempts to open a url with the specified browser. This is
+     * a utility method called by the openUrl methods.
+     *
+     * @param unixBrowser UnixBrowser
+     * @param urlString String
+     * @return boolean
+     * @throws BrowserLaunchingExecutionException
+     */
+    protected boolean openUrlWithBrowser(UnixBrowser unixBrowser,
+                                         String urlString)
+            throws BrowserLaunchingExecutionException {
+        boolean success = false;
+        logger.info(unixBrowser.getBrowserDisplayName());
+        logger.info(urlString);
+        try {
+            int exitCode = -1;
+            Process process = null;
+            String[] args;
+            // try to open in a new tab/current instance
+            // skip this attempt if force new window is set to true
+            if (!forceNewWindow) {
+                args = unixBrowser.getArgsForOpenBrowser(urlString);
+                if (logger.isDebugEnabled()) {
+                    logger.debug(Arrays.asList(args).toString());
+                }
+                process = Runtime.getRuntime().exec(args);
+                exitCode = process.waitFor();
+            }
+            // try call to force a new window if requested
+            if (forceNewWindow && exitCode != 0) {
+                args = unixBrowser.getArgsForForcingNewBrowserWindow(urlString);
+                if (logger.isDebugEnabled()) {
+                    logger.debug(Arrays.asList(args).toString());
+                }
+                process = Runtime.getRuntime().exec(args);
+                exitCode = process.waitFor();
+            }
+            // open in a new window
+            if (exitCode != 0) {
+                args = unixBrowser.getArgsForStartingBrowser(urlString);
+                if (logger.isDebugEnabled()) {
+                    logger.debug(Arrays.asList(args).toString());
+                }
+                process = Runtime.getRuntime().exec(args);
+                exitCode = process.waitFor();
+            }
+            if (exitCode == 0) {
+                success = true;
+            }
+        }
+        // Runtimes may throw InterruptedException
+        // want to catch every possible exception and wrap it
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+        return success;
+    }
+
+    /* ---------------------- from IBrowserLaunching ----------------------- */
+
+    /**
+     * Uses the which command to find out which browsers are available.
+     * The available browsers are put into the unixBrowsers map
+     * using displayName => StandardUnixBrowser mappings.
+     *
+     * @todo what do we do if there are no browsers available?
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        try {
+            URL configUrl = getClass().getResource(configFileName);
+            if (configUrl == null) {
+                throw new BrowserLaunchingInitializingException(
+                        "unable to find config file: " + configFileName);
+            }
+            StringBuffer potentialBrowserNames = new StringBuffer();
+            Properties configProps = new Properties();
+            configProps.load(configUrl.openStream());
+            String sepChar = configProps.getProperty(PROP_KEY_DELIMITER);
+            Iterator keysIter = configProps.keySet().iterator();
+            while (keysIter.hasNext()) {
+                String key = (String) keysIter.next();
+                if (key.startsWith(PROP_KEY_BROWSER_PREFIX)) {
+                    StandardUnixBrowser browser = new StandardUnixBrowser(
+                            sepChar,
+                            configProps.getProperty(key));
+                    if (browser.isBrowserAvailable(logger)) {
+                        unixBrowsers.put(browser.getBrowserDisplayName(),
+                                         browser);
+                    }
+                    else {
+                        if (potentialBrowserNames.length() > 0) {
+                            potentialBrowserNames.append("; ");
+                        }
+                        potentialBrowserNames.append(
+                                browser.getBrowserDisplayName());
+                    }
+                }
+            }
+            if (unixBrowsers.size() == 0) {
+                // no browser installed
+                throw new BrowserLaunchingInitializingException(
+                        "one of the supported browsers must be installed: "
+                        + potentialBrowserNames);
+            }
+            logger.info(unixBrowsers.keySet().toString());
+            unixBrowsers = Collections.unmodifiableMap(unixBrowsers);
+        }
+        catch (IOException ioex) {
+            throw new BrowserLaunchingInitializingException(ioex);
+        }
+    }
+
+    /**
+     * Opens a url in one of the available browsers.
+     *
+     * @param urlString String
+     * @throws BrowserLaunchingExecutionException
+     */
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        try {
+            logger.info(urlString);
+            boolean success = false;
+            // get list of browsers to try
+            List unixBrowsersList = new ArrayList(unixBrowsers.values());
+            // check system property which may contain user's preferred browser
+            String browserId = System.getProperty(
+                    IBrowserLaunching.BROWSER_SYSTEM_PROPERTY,
+                    null);
+            if (browserId != null) {
+                UnixBrowser unixBrowser =
+                        (UnixBrowser) unixBrowsers.get(browserId);
+                if (unixBrowser != null) {
+                    // if user has preferred browser, place at start of list
+                    unixBrowsersList.add(0, unixBrowser);
+                }
+            }
+            // iterate over browsers until one works
+            Iterator iter = unixBrowsersList.iterator();
+            UnixBrowser browser;
+            Process process;
+            while (iter.hasNext() && !success) {
+                browser = (UnixBrowser) iter.next();
+                success = openUrlWithBrowser(browser,
+                                             urlString);
+            }
+        }
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+    }
+
+    /**
+     * Opens a url in the specified browser. If the call to the
+     * specified browser fails, the method falls through to the
+     * non-targetted version.
+     *
+     * @param browser String
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String browser,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        UnixBrowser unixBrowser = (UnixBrowser) unixBrowsers.get(browser);
+        if (unixBrowser == null ||
+            IBrowserLaunching.BROWSER_DEFAULT.equals(browser)) {
+            logger.debug("falling through to non-targetted openUrl");
+            openUrl(urlString);
+        }
+        else {
+            boolean success = openUrlWithBrowser(unixBrowser,
+                                                 urlString);
+            if (!success) {
+                logger.debug(
+                        "open browser failure, trying non-targetted openUrl");
+                openUrl(urlString);
+            }
+        }
+    }
+
+    /**
+     * Allows user to target several browsers. The names of
+     * potential browsers can be accessed via the
+     * {@link #getBrowserList() getBrowserList} method.
+     * <p>
+     * The browsers from the list will be tried in order
+     * (first to last) until one of the calls succeeds. If
+     * all the calls to the requested browsers fail, the code
+     * will fail over to the default browser.
+     *
+     * @param browsers List
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(List browsers,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        if (browsers == null || browsers.isEmpty()) {
+            logger.debug("falling through to non-targetted openUrl");
+            openUrl(urlString);
+        }
+        else {
+            boolean success = false;
+            Iterator iter = browsers.iterator();
+            while (iter.hasNext() && !success) {
+                UnixBrowser unixBrowser = (UnixBrowser) unixBrowsers.get(
+                        iter.next());
+                if (unixBrowser != null) {
+                    success = openUrlWithBrowser(unixBrowser,
+                                                 urlString);
+                }
+            }
+            if (!success) {
+                logger.debug(
+                        "none of listed browsers succeeded; falling through to non-targetted openUrl");
+                openUrl(urlString);
+            }
+        }
+    }
+
+    /**
+     * Returns a list of browsers to be used for browser
+     * targetting. This list will always contain at least
+     * one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        List browsers = new ArrayList();
+        // add Default if not present
+        if (!unixBrowsers.containsKey(IBrowserLaunching.BROWSER_DEFAULT)) {
+            browsers.add(IBrowserLaunching.BROWSER_DEFAULT);
+        }
+        browsers.addAll(unixBrowsers.keySet());
+        return browsers;
+    }
+
+    /**
+     * Returns the policy used for opening a url in a browser.
+     * <p>
+     * If the policy is true, an attempt will be made to force the
+     * url to be opened in a new instance (window) of the
+     * browser.
+     * <p>
+     * If the policy is false, the url may open in a new window or
+     * a new tab.
+     * <p>
+     * Most browsers on Unix/Linux systems have command line options to
+     * support this feature.
+     *
+     * @return boolean
+     */
+    public boolean getNewWindowPolicy() {
+        return forceNewWindow;
+    }
+
+    /**
+     * Sets the policy used for opening a url in a browser.
+     *
+     * @param forceNewWindow boolean
+     */
+    public void setNewWindowPolicy(boolean forceNewWindow) {
+        this.forceNewWindow = forceNewWindow;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties b/source/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties
new file mode 100644
index 0000000..ff858fb
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/linuxUnixConfig.properties
@@ -0,0 +1,33 @@
+# ************************************************
+#    Copyright 2006,2007 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: linuxUnixConfig.properties,v 1.3 2007/06/13 19:25:54 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# list of browsers and arguments for using them
+# display name | executable name | start browser args | invoke already started browser | force new window
+browser.mozilla=Mozilla;mozilla;<browser> <url>;<browser> -remote openURL(<url>)
+browser.netscape=Netscape;netscape;<browser> <url>;<browser> -remote openURL(<url>)
+browser.firefox=FireFox;firefox;<browser> <url>;<browser> -new-tab <url>;<browser> -new-window <url>
+browser.mozilla-firefox=FireFox;mozilla-firefox;<browser> <url>;<browser> -remote openURL(<url>);<browser> -new-window <url>
+browser.konqueror=Konqueror;kfmclient;<browser> openURL <url>;<browser> newTab <url>;<browser> openURL <url>
+browser.opera=Opera;opera;<browser> <url>;<browser> -newpage <url>;<browser> -newwindow <url>
+browser.epiphany=Epiphany;epiphany;<browser> <url>;<browser> --new-tab <url>
diff --git a/source/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties b/source/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties
new file mode 100644
index 0000000..af32b42
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/misc/sunOSConfig.properties
@@ -0,0 +1,32 @@
+# ************************************************
+#    Copyright 2006 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: sunOSConfig.properties,v 1.2 2006/09/12 15:40:34 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# list of browsers and arguments for using them
+# display name | executable name | start browser args | invoke already started browser
+browser.sdtwebclient=Default;sdtwebclient;<browser> <url>;<browser> -remote openURL(<url>)
+browser.mozilla=Mozilla;mozilla;<browser> <url>;<browser> -remote openURL(<url>)
+browser.netscape=Netscape;netscape;<browser> <url>;<browser> -remote openURL(<url>)
+browser.firefox=FireFox;firefox;<browser> <url>;<browser> -remote openURL(<url>);<browser> -new-window <url>
+browser.opera=Opera;opera;<browser> <url>;<browser> -newpage <url>;<browser> -newwindow <url>
+
diff --git a/source/edu/stanford/ejalbert/launching/utils/LaunchingUtils.java b/source/edu/stanford/ejalbert/launching/utils/LaunchingUtils.java
new file mode 100644
index 0000000..a0d1088
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/utils/LaunchingUtils.java
@@ -0,0 +1,55 @@
+/************************************************
+    Copyright 2007 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: LaunchingUtils.java,v 1.1 2007/08/31 16:05:56 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.utils;
+
+/**
+ *
+ * @author not attributable
+ * @version 1.0
+ */
+public class LaunchingUtils {
+    private static final String REPLACE_BROWSER = "<browser>";
+    private static final String REPLACE_URL = "<url>";
+
+    public static String replaceArgs(String commands,
+                                     String browserArg,
+                                     String urlArg) {
+        // replace <browser> placeholder if browserArg passed
+        if(browserArg != null) {
+            commands = commands.replaceAll(REPLACE_BROWSER, browserArg);
+        }
+        // replace <url> placeholder if urlArg passed
+        if(urlArg != null) {
+            int urlPos = commands.indexOf(REPLACE_URL);
+            StringBuffer buf = new StringBuffer();
+            while (urlPos > 0) {
+                buf.append(commands.substring(0, urlPos));
+                buf.append(urlArg);
+                buf.append(commands.substring(urlPos + REPLACE_URL.length()));
+                commands = buf.toString();
+                urlPos = commands.indexOf(REPLACE_URL);
+                buf.setLength(0);
+            }
+        }
+        return commands;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/windows/WindowsBrowser.java b/source/edu/stanford/ejalbert/launching/windows/WindowsBrowser.java
new file mode 100644
index 0000000..be20bfe
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/windows/WindowsBrowser.java
@@ -0,0 +1,147 @@
+/************************************************
+    Copyright 2005,2006,2007 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: WindowsBrowser.java,v 1.6 2007/01/31 18:27:26 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.windows;
+
+import edu.stanford.ejalbert.launching.BrowserDescription;
+
+/**
+ * Encapsulates information on a Windows browser.
+ *
+ * @author Jeff Chapman
+ * @version 1.0
+ */
+public class WindowsBrowser
+        implements BrowserDescription {
+    /**
+     * The name for the browser suitable for display to a user.
+     */
+    private final String displayName; // in ctor
+    /**
+     * The name of the executable for the browser.
+     */
+    private final String exe; // in ctor
+    /**
+     * Arguments used in call to browser that will force the url to open in a new window rather than a new tab.
+     */
+    private final String forceWindowArgs; // in ctor
+    /**
+     * The path to the browsers executable. This is used to
+     * invoke the browser directly when using browser targetting.
+     */
+    private String pathToExe = null;
+    /**
+     * Name of the sub directory under Program Files in which
+     * the browser exe is typically installed.
+     * <p>
+     * This value is used as a secondary means of discovery if
+     * attempts with the Registry fail.
+     */
+    private final String subDirName;// in ctor
+
+    /**
+     * Splits the config string using the delimiter character and
+     * sets the display name; executable; new window argument; and
+     * Program Files sub directory name.
+     * <p>
+     * Sample config string (with ; as the delim char): displayName;exeName
+     *
+     * @param delimChar String
+     * @param configInfo String
+     */
+    WindowsBrowser(String delimChar,
+                   String configInfo) {
+        // we should always have four items in the config string
+        String[] configItems = configInfo.split(delimChar,4);
+        this.displayName = configItems[0];
+        this.exe = configItems[1];
+        this.forceWindowArgs = configItems[2];
+        this.subDirName = configItems[3];
+    }
+
+    /**
+     * Returns displayname and executable name for debugging.
+     *
+     * @return String
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append(displayName);
+        buf.append(": ForceWindowArg=");
+        buf.append(forceWindowArgs);
+        buf.append("; SubDir name=");
+        buf.append(subDirName);
+        buf.append("; Path to exe=");
+        if(pathToExe != null) {
+            buf.append(pathToExe);
+        }
+        buf.append("; Exe name=");
+        buf.append(exe);
+        return buf.toString();
+    }
+
+    void setPathToExe(String path) {
+        pathToExe = path;
+        if(!pathToExe.endsWith("\\")) {
+            StringBuffer buf = new StringBuffer(pathToExe.length() + 2);
+            buf.append(pathToExe);
+            buf.append('\\');
+            pathToExe = buf.toString();
+        }
+    }
+
+    String getPathToExe() {
+        return pathToExe;
+    }
+
+    String getSubDirName() {
+        return subDirName;
+    }
+
+    /* -------------------- from BrowserDescription ---------------------- */
+
+    /**
+     * Returns the display name for the browser.
+     *
+     * @return String
+     */
+    public String getBrowserDisplayName() {
+        return displayName;
+    }
+
+    /**
+     * Returns the name of the executable for the browser.
+     *
+     * @return String
+     */
+    public String getBrowserApplicationName() {
+        return exe;
+    }
+
+    /**
+     * Returns arguments used for forcing a new window. May be an empty String.
+     *
+     * @return String
+     */
+    public String getForceNewWindowArgs() {
+        return forceWindowArgs;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/windows/WindowsBrowserLaunching.java b/source/edu/stanford/ejalbert/launching/windows/WindowsBrowserLaunching.java
new file mode 100644
index 0000000..41de3af
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/windows/WindowsBrowserLaunching.java
@@ -0,0 +1,857 @@
+/************************************************
+    Copyright 2004,2005,2006,2007 Markus Gebhard, Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: WindowsBrowserLaunching.java,v 1.13 2007/08/31 15:54:10 jchapman0 Exp $
+package edu.stanford.ejalbert.launching.windows;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import at.jta.RegistryErrorException;
+import at.jta.Regor;
+import edu.stanford.ejalbert.exception.BrowserLaunchingExecutionException;
+import edu.stanford.ejalbert.exception.BrowserLaunchingInitializingException;
+import edu.stanford.ejalbert.exception.UnsupportedOperatingSystemException;
+import edu.stanford.ejalbert.launching.IBrowserLaunching;
+import net.sf.wraplog.AbstractLogger;
+import edu.stanford.ejalbert.launching.utils.LaunchingUtils;
+
+/**
+ * Handles initialization, configuration, and calls to open a url.
+ *
+ * @author Markus Gebhard, Jeff Chapman, Chris Dance
+ */
+public class WindowsBrowserLaunching
+        implements IBrowserLaunching {
+    /**
+     * windows configuration file -- info on commands and browsers
+     */
+    private static final String CONFIGFILE_WINDOWS =
+            "/edu/stanford/ejalbert/launching/windows/windowsConfig.properties";
+    /**
+     * config file key for Windows Vista
+     */
+    public static final String WINKEY_WINVISTA = "windows.winVista";
+    /**
+     * config file key for Windows 2000
+     */
+    public static final String WINKEY_WIN2000 = "windows.win2000";
+    /**
+     * config file key for Windows 9x
+     */
+    public static final String WINKEY_WIN9X = "windows.win9x";
+    /**
+     * config file key for Windows NT
+     */
+    public static final String WINKEY_WINNT = "windows.winNT";
+    /**
+     * collects valid config keys for key validation
+     */
+    private static final String[] WIN_KEYS = {
+                                             WINKEY_WIN2000,
+                                             WINKEY_WIN9X,
+                                             WINKEY_WINNT,
+                                             WINKEY_WINVISTA};
+    static {
+        Arrays.sort(WIN_KEYS);
+    }
+
+    protected final AbstractLogger logger; // in ctor
+
+    /**
+     * Maps display name and exe name to {@link WindowsBrowser WindowsBrowser}
+     * objects. Using name and exe as keys for backward compatiblity.
+     */
+    private Map browserNameAndExeMap = null;
+
+    /**
+     * List of {@link WindowsBrowser WindowsBrowser} objects that
+     * will be used to determine which browsers are available
+     * on the machine. The list is created from the windows
+     * config file.
+     */
+    private List browsersToCheck = new ArrayList();
+
+    /**
+     * Arguments for starting the default browser.
+     */
+    private String commandsDefaultBrowser; // in initialize
+    /**
+     * Arguments for starting a specific browser.
+     */
+    private String commandsTargettedBrowser; // in initialize
+    /**
+     * The key for accessing information from the windows config
+     * file for a particular version of windows.
+     * @see WINKEY_WIN2000
+     * @see WINKEY_WIN9X
+     * @see WINKEY_WINNT
+     */
+    private final String windowsKey; // in ctor
+    /**
+     * new window policy to apply when opening a url. If true,
+     * try to force url into a new browser instance/window.
+     */
+    private boolean forceNewWindow = false;
+
+    /**
+     * set from properties to determine if the registry
+     * should be consulted for available browsers.
+     * Vista does not allow universal access to the registry.
+     */
+    private boolean useRegistry = false;
+
+    private String programFilesFolderTemplate;
+    private String driveLetters;
+
+    // constants defined for accessing and processing registry information
+    //private static final int REGEDIT_TYPE_APPPATHS = 0;
+    //private static final int REGEDIT_TYPE_UNINSTALL = 1;
+    //private static String[] regeditQueries = {
+    //                                         "\"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\""};
+    //"\"HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\""};
+
+    /**
+     * Checks that the windows key is valid.
+     *
+     * @param logger AbstractLogger
+     * @param windowsKey String
+     */
+    public WindowsBrowserLaunching(AbstractLogger logger,
+                                   String windowsKey) {
+        if (windowsKey == null) {
+            throw new IllegalArgumentException("windowsKey cannot be null");
+        }
+        if (Arrays.binarySearch(WIN_KEYS, windowsKey) < 0) {
+            throw new IllegalArgumentException(windowsKey + " is invalid");
+        }
+        this.logger = logger;
+        this.windowsKey = windowsKey;
+        logger.info(windowsKey);
+    }
+
+    private String getArrayAsString(String[] array) {
+        return Arrays.asList(array).toString();
+    }
+
+    /**
+     * Returns the protocol for the url.
+     *
+     * @param urlString String
+     * @return String
+     * @throws MalformedURLException
+     */
+    private String getProtocol(String urlString)
+            throws MalformedURLException {
+        URL url = new URL(urlString);
+        return url.getProtocol();
+    }
+
+    /**
+     * Returns map of browser names and exe names to
+     * {@link WindowsBrowser WindowsBrowser} objects.
+     * <p>
+     * This is the preferred method for accessing the browser name and exe map.
+     * @return Map
+     */
+    private Map getBrowserMap() {
+        // Handles lazy instantiation of available browser map.
+        synchronized (WindowsBrowserLaunching.class) {
+            if (browserNameAndExeMap == null) {
+                browserNameAndExeMap = new HashMap();
+                // pull additional browsers from system property??
+                // ---------
+                // create temporary list of browsers to check to track which
+                // ones have been found
+                // we will remove items from this temp list
+                List tempBrowsersToCheck = new ArrayList(browsersToCheck);
+                // first try the registry
+                if (useRegistry) {
+                    browserNameAndExeMap.putAll(
+                            getAvailableBrowsers(tempBrowsersToCheck));
+                }
+                // if there are still browsers to find, try file path
+                if (!tempBrowsersToCheck.isEmpty()) {
+                    browserNameAndExeMap.putAll(
+                            processFilePathsForBrowsers(tempBrowsersToCheck));
+                }
+            }
+        }
+        return browserNameAndExeMap;
+    }
+
+    /**
+     * Use program files folder template from properties file and
+     * the list of drive letters from that properties file
+     * @return File
+     */
+    private File getProgramFilesPath() {
+        File progFilesPath = null;
+        if (driveLetters != null && programFilesFolderTemplate != null) {
+            String[] drives = driveLetters.split(";");
+            for (int idx = 0; idx < drives.length && progFilesPath == null; idx++) {
+                String path = MessageFormat.format(
+                        programFilesFolderTemplate,
+                        new Object[] {drives[idx]});
+                File pfPath = new File(path);
+                logger.debug(path);
+                logger.debug(pfPath.getPath());
+                if (pfPath.exists()) {
+                    progFilesPath = pfPath;
+                }
+            }
+        }
+        return progFilesPath;
+    }
+
+    /**
+     * Secondary method for browser discovery.
+     * <p>
+     * Uses IE to get the path to the Program Files directory;
+     * then gets a list of the sub dirs and checks them against
+     * the remaining browsers.
+     *
+     * @param iePath String
+     * @param browsersAvailable Map
+     * @param tmpBrowsersToCheck List
+     */
+    private Map processFilePathsForBrowsers(
+            List tmpBrowsersToCheck) {
+        logger.debug("finding available browsers in program files path");
+        logger.debug("browsers to check: " + tmpBrowsersToCheck);
+        Map browsersAvailable = new HashMap();
+        File progFilesPath = getProgramFilesPath();
+        if (progFilesPath != null) {
+            logger.debug("program files path: " + progFilesPath.getPath());
+            File[] subDirs = progFilesPath.listFiles(new DirFileFilter());
+            int subDirsCnt = subDirs != null ? subDirs.length : 0;
+            // create and populate map of dir names to win browser objects
+            Iterator iter = tmpBrowsersToCheck.iterator();
+            Map dirNameToBrowser = new HashMap();
+            while (iter.hasNext()) {
+                WindowsBrowser wBrowser = (WindowsBrowser) iter.next();
+                dirNameToBrowser.put(wBrowser.getSubDirName(), wBrowser);
+            }
+            // iterate over subdirs and compare to map entries
+            for (int idx = 0; idx < subDirsCnt && !tmpBrowsersToCheck.isEmpty();
+                           idx++) {
+                if (dirNameToBrowser.containsKey(subDirs[idx].getName())) {
+                    WindowsBrowser wBrowser = (WindowsBrowser) dirNameToBrowser.
+                                              get(
+                            subDirs[idx].getName());
+                    // need to search folder and sub-folders for exe to find
+                    // the full path
+                    String exeName = wBrowser.getBrowserApplicationName() +
+                                     ".exe";
+                    File fullPathToExe = findExeFilePath(
+                            subDirs[idx],
+                            exeName);
+                    if (fullPathToExe != null) {
+                        logger.debug("Adding browser " +
+                                     wBrowser.getBrowserDisplayName() +
+                                     " to available list.");
+                        wBrowser.setPathToExe(fullPathToExe.getPath());
+                        logger.debug(wBrowser.getPathToExe());
+                        // adding display and exe for backward compatibility and
+                        // ease of use if someone passes in the name of an exe
+                        browsersAvailable.put(wBrowser.getBrowserDisplayName(),
+                                              wBrowser);
+                        browsersAvailable.put(wBrowser.
+                                              getBrowserApplicationName(),
+                                              wBrowser);
+                        tmpBrowsersToCheck.remove(wBrowser);
+                    }
+                }
+            }
+        }
+        return browsersAvailable;
+    }
+
+    private File findExeFilePath(File path, String exeName) {
+        File exePath = null;
+        File exeFiles[] = path.listFiles(new ExeFileNameFilter());
+        if (exeFiles != null && exeFiles.length > 0) {
+            for (int idx = 0; idx < exeFiles.length && exePath == null; idx++) {
+                if (exeFiles[idx].getName().equalsIgnoreCase(exeName)) {
+                    // found the exe, get parent
+                    exePath = exeFiles[idx].getParentFile();
+                }
+            }
+        }
+        // didn't find the exe
+        if (exePath == null) {
+            File[] subDirs = path.listFiles(new DirFileFilter());
+            if (subDirs != null && subDirs.length > 0) {
+                for (int idx = 0; idx < subDirs.length && exePath == null; idx++) {
+                    exePath = findExeFilePath(subDirs[idx], exeName);
+                }
+            }
+        }
+        return exePath;
+    }
+
+    /**
+     * Filter used to only select directories.
+     */
+    private static final class DirFileFilter
+            implements FileFilter {
+        public boolean accept(File pathname) {
+            return pathname.isDirectory();
+        }
+    }
+
+
+    /**
+     * Filter used to only find exe files.
+     */
+    private static final class ExeFileNameFilter
+            implements FilenameFilter {
+        public boolean accept(File dir, String name) {
+            return name.toLowerCase().endsWith(".exe");
+        }
+    }
+
+
+    private Map getExeNamesToBrowsers(List tempBrowsersToCheck) {
+        Map exeNamesToBrowsers = new HashMap();
+        Iterator iter = tempBrowsersToCheck.iterator();
+        while (iter.hasNext()) {
+            WindowsBrowser winBrowser = (WindowsBrowser) iter.next();
+            String exeName = winBrowser.getBrowserApplicationName().
+                             toLowerCase() + ".exe";
+            exeNamesToBrowsers.put(exeName, winBrowser);
+        }
+        return exeNamesToBrowsers;
+    }
+
+    private WindowsBrowser getBrowserFromRegistryEntry(
+            Regor regor,
+            int key,
+            String subKey,
+            String exeKey,
+            Map exesToBrowserObjs)
+            throws RegistryErrorException {
+        WindowsBrowser winBrowser = null;
+        int key2 = regor.openKey(key, subKey);
+        List values = regor.listValueNames(key2);
+        //boolean fndPath = false;
+        for (int x = 0;
+                     values != null && x < values.size() && winBrowser == null;
+                     x++) {
+            byte[] buf = regor.readValue(
+                    key2,
+                    (String) values.get(x));
+            String path = buf != null ? Regor.parseValue(buf) :
+                          "";
+            String lpath = path.toLowerCase();
+            if (lpath.endsWith(exeKey)) {
+                winBrowser = (WindowsBrowser)
+                             exesToBrowserObjs.get(exeKey);
+                // get path to exe and set it in winBrowser object
+                StringTokenizer tokenizer =
+                        new StringTokenizer(path, "\\", false);
+                StringBuffer pathBuf = new StringBuffer();
+                int tokCnt = tokenizer.countTokens();
+                // we want to ignore the last token
+                for (int idx = 1; idx < tokCnt; idx++) {
+                    pathBuf.append(tokenizer.nextToken());
+                    pathBuf.append('\\');
+                }
+                winBrowser.setPathToExe(pathBuf.toString());
+            }
+        }
+        return winBrowser;
+    }
+
+    /**
+     * Accesses the Windows registry to look for browser exes. The
+     * browsers search for are in the browsersToCheck list. The returned
+     * map will use display names and exe names as keys to the
+     * {@link WindowsBrowser WindowsBrowser} objects.
+     *
+     * @param browsersToCheck List
+     * @return Map
+     */
+    private Map getAvailableBrowsers(List tempBrowsersToCheck) {
+        logger.debug("finding available browsers using registry");
+        logger.debug("browsers to check: " + tempBrowsersToCheck);
+        Map browsersAvailable = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+        try {
+            // create map of exe names to win browser objects
+            Map exesToBrowserObjs = getExeNamesToBrowsers(tempBrowsersToCheck);
+            // access and look in registry
+            Regor regor = new Regor();
+            String subKeyName =
+                    "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths";
+            int key = regor.openKey(Regor.HKEY_LOCAL_MACHINE,
+                                    subKeyName);
+            if (key > -1) {
+                List keys = regor.listKeys(key);
+                Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);
+                Iterator keysIter = exesToBrowserObjs.keySet().iterator();
+                while (keysIter.hasNext()) {
+                    String exeKey = (String) keysIter.next();
+                    int index = Collections.binarySearch(
+                            keys,
+                            exeKey,
+                            String.CASE_INSENSITIVE_ORDER);
+                    if (index >= 0) {
+                        WindowsBrowser winBrowser = getBrowserFromRegistryEntry(
+                                regor,
+                                key,
+                                (String) keys.get(index),
+                                exeKey,
+                                exesToBrowserObjs);
+                        if (winBrowser != null) {
+                            if (logger.isDebugEnabled()) {
+                                logger.debug("Adding browser " +
+                                             winBrowser.
+                                             getBrowserDisplayName() +
+                                             " to available list.");
+                                logger.debug(winBrowser.getPathToExe());
+                            }
+                            // adding display and exe for backward compatibility and
+                            // ease of use if someone passes in the name of an exe
+                            browsersAvailable.put(winBrowser.
+                                                  getBrowserDisplayName(),
+                                                  winBrowser);
+                            browsersAvailable.put(winBrowser.
+                                                  getBrowserApplicationName(),
+                                                  winBrowser);
+                            tempBrowsersToCheck.remove(winBrowser);
+                        }
+                    }
+                }
+            }
+        }
+        catch (RegistryErrorException ex) {
+            logger.error("problem accessing registry", ex);
+        }
+        return browsersAvailable;
+    }
+
+    /**
+     * Returns the windows arguments for launching a default browser.
+     *
+     * @param protocol String
+     * @param urlString String
+     * @return String[]
+     */
+    private String[] getCommandArgs(String protocol,
+                                    String urlString) {
+        String commandArgs = LaunchingUtils.replaceArgs(
+                commandsDefaultBrowser,
+                null,
+                urlString);
+        return commandArgs.split("[ ]");
+    }
+
+    /**
+     * Returns the windows arguments for launching a specified browser.
+     * <p>
+     * Depending on the forceNewWindow boolean, the args may also contain the
+     * args to force a new window.
+     *
+     * @param protocol String
+     * @param winbrowser WindowsBrowser
+     * @param urlString String
+     * @param forceNewWindow boolean
+     * @return String[]
+     */
+    private String getCommandArgs(String protocol,
+                                  WindowsBrowser winbrowser,
+                                  String urlString,
+                                  boolean forceNewWindow) {
+        String commandArgs = LaunchingUtils.replaceArgs(
+                commandsTargettedBrowser,
+                winbrowser.getBrowserApplicationName(),
+                urlString);
+        String args = "";
+        if (forceNewWindow) {
+            args = winbrowser.getForceNewWindowArgs();
+        }
+        commandArgs = commandArgs.replaceAll("<args>", args);
+        int pathLoc = commandArgs.indexOf("<path>");
+        if (pathLoc > 0) {
+            StringBuffer buf = new StringBuffer();
+            buf.append(commandArgs.substring(0, pathLoc));
+            buf.append(winbrowser.getPathToExe());
+            buf.append(commandArgs.substring(pathLoc + 6));
+            commandArgs = buf.toString();
+        }
+        return commandArgs; //.split("[ ]");
+    }
+
+    /**
+     * Attempts to open a url with the specified browser. This is
+     * a utility method called by the openUrl methods.
+     *
+     * @param winBrowser WindowsBrowser
+     * @param protocol String
+     * @param urlString String
+     * @return boolean
+     * @throws BrowserLaunchingExecutionException
+     */
+    private boolean openUrlWithBrowser(WindowsBrowser winBrowser,
+                                       String protocol,
+                                       String urlString)
+            throws BrowserLaunchingExecutionException {
+        boolean success = false;
+        try {
+            logger.info(winBrowser.getBrowserDisplayName());
+            logger.info(urlString);
+            logger.info(protocol);
+            String args = getCommandArgs(
+                    protocol,
+                    winBrowser,
+                    urlString,
+                    forceNewWindow);
+            if (logger.isDebugEnabled()) {
+                logger.debug(args);
+            }
+            Process process = Runtime.getRuntime().exec(args);
+            // This avoids a memory leak on some versions of Java on Windows.
+            // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>.
+            process.waitFor();
+            // some browsers (mozilla, firefox) return 1 if you attempt to
+            // open a url and an instance of that browser is already running
+            // not clear why because the call is succeeding, ie the browser
+            // opens the url.
+            // If we don't say 1 is also a success, we get two browser
+            // windows or tabs opened to the url.
+            //
+            // We could make this check smarter in the future if we run
+            // into problems. the winBrowser object could handle the
+            // check to make it browser specific.
+            int exitValue = process.exitValue();
+            success = exitValue == 0 || exitValue == 1;
+        }
+        // Runtimes may throw InterruptedException
+        // want to catch every possible exception and wrap it
+        catch (Exception e) {
+            throw new BrowserLaunchingExecutionException(e);
+        }
+        return success;
+    }
+
+    /* ----------------- from IBrowserLaunching -------------------- */
+
+    /**
+     * Initializes the browser launcher from the windows config
+     * file. It initializes the browsers to check list and
+     * the command line args to use for version of windows
+     * referenced by the windowsKey.
+     *
+     * @see windowsKey
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void initialize()
+            throws BrowserLaunchingInitializingException {
+        try {
+            URL configUrl = getClass().getResource(CONFIGFILE_WINDOWS);
+            if (configUrl == null) {
+                throw new BrowserLaunchingInitializingException(
+                        "unable to find config file: " + CONFIGFILE_WINDOWS);
+            }
+            Properties configProps = new Properties();
+            configProps.load(configUrl.openStream());
+            // get sep char
+            String sepChar = configProps.getProperty(PROP_KEY_DELIMITER);
+            // load different types of browsers
+            Iterator keysIter = configProps.keySet().iterator();
+            while (keysIter.hasNext()) {
+                String key = (String) keysIter.next();
+                if (key.startsWith(PROP_KEY_BROWSER_PREFIX)) {
+                    WindowsBrowser winBrowser = new WindowsBrowser(
+                            sepChar,
+                            configProps.getProperty(key));
+                    browsersToCheck.add(winBrowser);
+                }
+            }
+            // load the type of windows based on the windows key
+            String windowsConfigStr = configProps.getProperty(
+                    windowsKey,
+                    null);
+            if (windowsConfigStr == null) {
+                throw new BrowserLaunchingInitializingException(
+                        windowsKey + " is not a valid property");
+            }
+            String[] winConfigItems = windowsConfigStr.split(sepChar);
+            commandsDefaultBrowser = winConfigItems[0];
+            commandsTargettedBrowser = winConfigItems[1];
+            Boolean boolVal = new Boolean(winConfigItems[2]);
+            useRegistry = boolVal.booleanValue();
+            // check for override of useRegistry from system prop
+            // need to explicitly check BOTH values to filter out
+            // invalid prop values
+            String propValue = System.getProperty(
+                    IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_PROPERTY,
+                    null);
+            if (IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_DISK.equals(
+                    propValue)) {
+                useRegistry = false;
+            }
+            else if (IBrowserLaunching.WINDOWS_BROWSER_DISC_POLICY_REGISTRY.
+                     equals(propValue)) {
+                useRegistry = true;
+            }
+            if (logger.isDebugEnabled()) {
+                logger.debug("Browser discovery policy property value=" +
+                             (propValue == null ? "null" : propValue));
+                logger.debug("useRegistry=" + Boolean.toString(useRegistry));
+            }
+            // get info for checking Program Files folder
+            programFilesFolderTemplate = configProps.getProperty(
+                    "program.files.template",
+                    null);
+            driveLetters = configProps.getProperty(
+                    "drive.letters",
+                    null);
+            // set brwosersToCheck to a non-modifiable list
+            browsersToCheck = Collections.unmodifiableList(browsersToCheck);
+        }
+        catch (IOException ioex) {
+            throw new BrowserLaunchingInitializingException(ioex);
+        }
+    }
+
+    /**
+     * Opens a url using the default browser.
+     *
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        try {
+            logger.info(urlString);
+            String protocol = getProtocol(urlString);
+            logger.info(protocol);
+            // try the system prop first
+            boolean successfullSystemPropLaunch = false;
+            String browserName = System.getProperty(
+                    IBrowserLaunching.BROWSER_SYSTEM_PROPERTY,
+                    null);
+            if (browserName != null) {
+                Map browserMap = getBrowserMap();
+                WindowsBrowser winBrowser = (WindowsBrowser) browserMap.get(
+                        browserName);
+                if (winBrowser != null) {
+                    logger.debug("using browser from system property");
+                    successfullSystemPropLaunch = openUrlWithBrowser(
+                            winBrowser,
+                            protocol,
+                            urlString);
+                }
+            }
+            if (!successfullSystemPropLaunch) {
+                String[] args = getCommandArgs(protocol,
+                                               urlString);
+                if (logger.isDebugEnabled()) {
+                    logger.debug(getArrayAsString(args));
+                }
+                Process process = Runtime.getRuntime().exec(args);
+                // This avoids a memory leak on some versions of Java on Windows.
+                // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>.
+                process.waitFor();
+                process.exitValue();
+            }
+        }
+        catch (Exception e) {
+            logger.error("fatal exception", e);
+            throw new BrowserLaunchingExecutionException(e);
+        }
+    }
+
+    /**
+     * Opens a url using a specific browser.
+     * <p>
+     * If the specified browser is not available, the method will
+     * fall through to calling the default openUrl method.
+     *
+     * @param browser String
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(String browser,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        if (IBrowserLaunching.BROWSER_DEFAULT.equals(browser) ||
+            browser == null) {
+            logger.info(
+                    "default or null browser target; falling through to non-targetted openUrl");
+            openUrl(urlString);
+        }
+        else {
+            Map browserMap = getBrowserMap();
+            WindowsBrowser winBrowser = (WindowsBrowser) browserMap.get(browser);
+            if (winBrowser == null) {
+                logger.info("the available browsers list does not contain: " +
+                            browser);
+                logger.info("falling through to non-targetted openUrl");
+                openUrl(urlString);
+            }
+            else {
+                String protocol = null;
+                try {
+                    protocol = getProtocol(urlString);
+                }
+                catch (MalformedURLException malrulex) {
+                    throw new BrowserLaunchingExecutionException(malrulex);
+                }
+                boolean successfullLaunch = openUrlWithBrowser(
+                        winBrowser,
+                        protocol,
+                        urlString);
+                if (!successfullLaunch) {
+                    logger.debug("falling through to non-targetted openUrl");
+                    openUrl(urlString);
+                }
+            }
+        }
+    }
+
+    /**
+     * Allows user to target several browsers. The names of
+     * potential browsers can be accessed via the
+     * {@link #getBrowserList() getBrowserList} method.
+     * <p>
+     * The browsers from the list will be tried in order
+     * (first to last) until one of the calls succeeds. If
+     * all the calls to the requested browsers fail, the code
+     * will fail over to the default browser.
+     *
+     * @param browsers List
+     * @param urlString String
+     * @throws UnsupportedOperatingSystemException
+     * @throws BrowserLaunchingExecutionException
+     * @throws BrowserLaunchingInitializingException
+     */
+    public void openUrl(List browsers,
+                        String urlString)
+            throws UnsupportedOperatingSystemException,
+            BrowserLaunchingExecutionException,
+            BrowserLaunchingInitializingException {
+        if (browsers == null || browsers.isEmpty()) {
+            logger.debug("falling through to non-targetted openUrl");
+            openUrl(urlString);
+        }
+        else {
+            String protocol = null;
+            try {
+                protocol = getProtocol(urlString);
+            }
+            catch (MalformedURLException malrulex) {
+                throw new BrowserLaunchingExecutionException(malrulex);
+            }
+            Map browserMap = getBrowserMap();
+            boolean success = false;
+            Iterator iter = browsers.iterator();
+            while (iter.hasNext() && !success) {
+                WindowsBrowser winBrowser = (WindowsBrowser) browserMap.get(
+                        iter.next());
+                if (winBrowser != null) {
+                    success = openUrlWithBrowser(winBrowser,
+                                                 protocol,
+                                                 urlString);
+                }
+            }
+            if (!success) {
+                logger.debug(
+                        "none of listed browsers succeeded; falling through to non-targetted openUrl");
+                openUrl(urlString);
+            }
+        }
+    }
+
+
+    /**
+     * Returns a list of browsers to be used for browser targetting.
+     * This list will always contain at least one item--the BROWSER_DEFAULT.
+     *
+     * @return List
+     */
+    public List getBrowserList() {
+        Map browserMap = getBrowserMap();
+        List browsers = new ArrayList();
+        browsers.add(IBrowserLaunching.BROWSER_DEFAULT);
+        // exes are present in the map as well as display names
+        Iterator iter = browserMap.keySet().iterator();
+        while (iter.hasNext()) {
+            String key = (String) iter.next();
+            WindowsBrowser winBrowser = (WindowsBrowser) browserMap.get(key);
+            if (key.equals(winBrowser.getBrowserDisplayName())) {
+                browsers.add(winBrowser.getBrowserDisplayName());
+            }
+        }
+        return browsers;
+    }
+
+    /**
+     * Returns the policy used for opening a url in a browser.
+     * <p>
+     * If the policy is true, an attempt will be made to force the
+     * url to be opened in a new instance (window) of the
+     * browser.
+     * <p>
+     * If the policy is false, the url may open in a new window or
+     * a new tab.
+     * <p>
+     * Some browsers on Windows systems have command line options to
+     * support this feature.
+     *
+     * @return boolean
+     */
+    public boolean getNewWindowPolicy() {
+        return forceNewWindow;
+    }
+
+    /**
+     * Sets the policy used for opening a url in a browser.
+     *
+     * @param forceNewWindow boolean
+     */
+    public void setNewWindowPolicy(boolean forceNewWindow) {
+        this.forceNewWindow = forceNewWindow;
+    }
+}
diff --git a/source/edu/stanford/ejalbert/launching/windows/windowsConfig.properties b/source/edu/stanford/ejalbert/launching/windows/windowsConfig.properties
new file mode 100644
index 0000000..abdcd2e
--- /dev/null
+++ b/source/edu/stanford/ejalbert/launching/windows/windowsConfig.properties
@@ -0,0 +1,48 @@
+# ************************************************
+#    Copyright 2006,2007 Jeff Chapman
+#
+#    This file is part of BrowserLauncher2.
+#
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+#
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# ************************************************
+# $Id: windowsConfig.properties,v 1.6 2007/08/30 19:38:09 jchapman0 Exp $
+
+# delimiter for browser listing
+delimchar=;
+
+# windows versions and arguments for launching a browser
+# command for starting default browser | command for starting a specific browser | use registry
+windows.winVista=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.win2000=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.win9x=command.com /c start "<url>";"<path><browser>.exe" <args> "<url>";true
+windows.winNT=cmd.exe /c start "" "<url>";"<path><browser>.exe" <args> "<url>";true
+
+# properties used to find browsers in program files folder
+program.files.template={0}:\\Program Files
+# drive letters to try when looking for Program Files folder
+drive.letters=C;D;E
+
+#windows.win2000=cmd.exe /c start "" "<url>";cmd.exe /c start <browser> <args> "<url>"
+#windows.win9x=command.com /c start "<url>";command.com /c start <browser> <args> "<url>"
+#windows.winNT=cmd.exe /c start "" "<url>";cmd.exe /c start <browser> <args> "<url>"
+
+# list of browsers and arguments for using them and discovering them
+# browser display name | browser exe name | new window argument | directory containing exe
+browser.mozilla=Mozilla;mozilla;;mozilla.org
+browser.netscape=Netscape;netscape;;Netscape
+browser.firefox=FireFox;firefox;-new-window;Mozilla Firefox
+browser.opera=Opera;opera;-newwindow;Opera
+browser.ie=IE;iexplore;;Internet Explorer
+browser.kmeleon=K-Meleon;k-meleon;;K-Meleon
diff --git a/source/edu/stanford/ejalbert/resources/Debugging.properties b/source/edu/stanford/ejalbert/resources/Debugging.properties
new file mode 100644
index 0000000..c80e945
--- /dev/null
+++ b/source/edu/stanford/ejalbert/resources/Debugging.properties
@@ -0,0 +1,59 @@
+# ************************************************
+#    Copyright 2005 Jeff Chapman
+
+#    This file is part of BrowserLauncher2.
+
+#    BrowserLauncher2 is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU Lesser General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+
+#    BrowserLauncher2 is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Lesser General Public License for more details.
+
+#    You should have received a copy of the GNU Lesser General Public License
+#    along with BrowserLauncher2; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+# ************************************************/
+# $Id: Debugging.properties,v 1.8 2007/01/31 18:31:22 jchapman0 Exp $
+
+# Resource file containing strings and other info for use by the Testing
+# tool.
+
+# initial message to display
+debug.mssg=\
+Please let us know the results of testing on your OS/Java configuration. The \
+information displayed below as well as any stack traces will be \
+helpful. Use the copy button to copy and paste the data into an email.
+
+# system properties to query and display, | and ; as separators
+# display|property.name;
+debug.propnames=\
+Java Version: |java.version;\
+Java Vendor: |java.vendor;\
+OS Name: |os.name;\
+OS Version: |os.version
+
+label.url=Enter a url:
+label.app.title=BrowserLauncher2 Test App 1.0
+label.logging.level=Logging Level:
+label.browser.list=Browser List:
+label.window.policy=Force New Window
+
+bttn.browse=Browse
+bttn.copy=Copy
+bttn.set.logging=Set Logging Level
+bttn.set.preference=Set Browser Preference
+
+logging.level.select.title=Logging Level Selection
+logging.level.select.message=Select a logging level from the list below
+
+url.default=file://localhost/
+
+# properties for logging interface
+logging.dateformat=yyyy-MM-dd HH:mm:ss,SSS
+
+logging.level.labels=DEBUG;INFO;WARN;ERROR
diff --git a/source/edu/stanford/ejalbert/testing/BrowserLauncherTestApp.java b/source/edu/stanford/ejalbert/testing/BrowserLauncherTestApp.java
new file mode 100644
index 0000000..5185d63
--- /dev/null
+++ b/source/edu/stanford/ejalbert/testing/BrowserLauncherTestApp.java
@@ -0,0 +1,349 @@
+/************************************************
+    Copyright 2004,2005,2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: BrowserLauncherTestApp.java,v 1.22 2007/01/31 18:32:41 jchapman0 Exp $
+package edu.stanford.ejalbert.testing;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+import java.util.Arrays;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+
+import edu.stanford.ejalbert.BrowserLauncher;
+import edu.stanford.ejalbert.exceptionhandler.BrowserLauncherErrorHandler;
+import java.awt.GridLayout;
+import java.util.List;
+import javax.swing.JCheckBox;
+import java.awt.event.ItemListener;
+import java.awt.event.ItemEvent;
+import edu.stanford.ejalbert.browserprefui.BrowserPrefAction;
+
+/**
+ * Standalone gui that allows for testing the broserlauncher code and provides
+ * a sample implementation.
+ *
+ * @author Jeff Chapman
+ */
+public class BrowserLauncherTestApp
+        extends JFrame {
+    private static final String debugResources =
+            "edu.stanford.ejalbert.resources.Debugging";
+    private TestAppLogger logger; // in ctor
+    private JComboBox browserBox = new JComboBox();
+    private JLabel loggingLevelTxtFld = new JLabel();
+    private JTextField urlTextField = new JTextField();
+    private BrowserLauncher launcher; // in ctor
+    private JTextArea debugTextArea = new JTextArea();
+    private JTextField browserListField = new JTextField();
+    private ResourceBundle bundle; // in ctor
+    private JCheckBox windowPolicyCBox = new JCheckBox();
+
+    public BrowserLauncherTestApp() {
+        super();
+        try {
+            bundle = ResourceBundle.getBundle(debugResources);
+            logger = initDebugLogging();
+            loggingLevelTxtFld.setText(logger.getLevelText());
+            super.setTitle(bundle.getString("label.app.title"));
+            populateDebugInfo(bundle, debugTextArea);
+            launcher = new BrowserLauncher(
+                    logger,
+                    new TestAppErrorHandler(debugTextArea));
+            ComboBoxModel cbModel = new DefaultComboBoxModel(launcher.
+                    getBrowserList().toArray());
+            browserBox.setModel(cbModel);
+            windowPolicyCBox.setSelected(launcher.getNewWindowPolicy());
+            jbInit();
+        }
+        catch (Exception ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    private TestAppLogger initDebugLogging() {
+        String[] levelLabels =
+                bundle.getString("logging.level.labels").split(";");
+        debugTextArea.setEditable(false);
+        debugTextArea.setLineWrap(true);
+        debugTextArea.setWrapStyleWord(true);
+        debugTextArea.setText("");
+        return new TestAppLogger(debugTextArea,
+                                 levelLabels,
+                                 bundle.getString("logging.dateformat"));
+    }
+
+    public static void main(String[] args) {
+        BrowserLauncherTestApp app = new BrowserLauncherTestApp();
+        app.pack();
+        app.setVisible(true);
+    }
+
+    private void windowPolicyItemStateChange(ItemEvent e) {
+        launcher.setNewWindowPolicy(e.getStateChange() == ItemEvent.SELECTED);
+    }
+
+    private void populateDebugInfo(ResourceBundle bundle,
+                                   JTextArea debugTextArea) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter, true);
+        // display first message
+        printWriter.println(bundle.getString("debug.mssg"));
+        printWriter.println();
+        // get property values to display
+        StringTokenizer tokenizer =
+                new StringTokenizer(bundle.getString("debug.propnames"),
+                                    ";",
+                                    false);
+        int pipeSymbol;
+        String token, display, property;
+        while (tokenizer.hasMoreTokens()) {
+            token = tokenizer.nextToken();
+            pipeSymbol = token.indexOf('|');
+            display = token.substring(0, pipeSymbol);
+            property = token.substring(pipeSymbol + 1);
+            printWriter.print(display);
+            printWriter.println(System.getProperty(property));
+        }
+        printWriter.close();
+        debugTextArea.append(stringWriter.toString());
+    }
+
+    private void jbInit()
+            throws Exception {
+        // button and action for setting the browser preference
+        BrowserPrefAction browserPrefAction = new BrowserPrefAction(
+                bundle.getString("bttn.set.preference"),
+                launcher,
+                this);
+        JButton prefBrowserBttn = new JButton(browserPrefAction);
+
+        JButton browseButton = new JButton(bundle.getString("bttn.browse"));
+        browseButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                browseButton_actionPerformed(e);
+            }
+        });
+        JLabel enterUrlLabel = new JLabel(bundle.getString("label.url"));
+        urlTextField.setText(bundle.getString("url.default"));
+        urlTextField.setColumns(25);
+        JPanel urlPanel = new JPanel(new BorderLayout());
+        urlPanel.add(enterUrlLabel, BorderLayout.LINE_START);
+        urlPanel.add(urlTextField, BorderLayout.CENTER);
+        urlPanel.add(browseButton, BorderLayout.LINE_END);
+
+        JScrollPane debugTextScrollPane = new JScrollPane(debugTextArea);
+        //debugTextScrollPane.getViewport().add(debugTextArea);
+
+        JLabel debugLevelLabel = new JLabel(bundle.getString(
+                "label.logging.level"));
+        JButton loggingLevelBttn = new JButton(bundle.getString(
+                "bttn.set.logging"));
+        loggingLevelBttn.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                loggingLevelBttn_actionPerformed(e);
+            }
+        });
+        JButton copyButton = new JButton(bundle.getString("bttn.copy"));
+        copyButton.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                copyButton_actionPerformed(e);
+            }
+        });
+        JPanel debugTextBttnPanel = new JPanel();
+        BoxLayout bttnBoxLayout = new BoxLayout(
+                debugTextBttnPanel,
+                BoxLayout.X_AXIS);
+        debugTextBttnPanel.setLayout(bttnBoxLayout);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(2));
+        debugTextBttnPanel.add(browserBox);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(2));
+        debugTextBttnPanel.add(debugLevelLabel);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(3));
+        debugTextBttnPanel.add(loggingLevelTxtFld);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(5));
+        debugTextBttnPanel.add(Box.createHorizontalGlue());
+        debugTextBttnPanel.add(loggingLevelBttn);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(3));
+        debugTextBttnPanel.add(copyButton);
+        debugTextBttnPanel.add(Box.createHorizontalStrut(2));
+
+        windowPolicyCBox.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                windowPolicyItemStateChange(e);
+            }
+        });
+        windowPolicyCBox.setText(bundle.getString("label.window.policy"));
+        JLabel browserListLabel = new JLabel(bundle.getString(
+                "label.browser.list"));
+        JPanel browserListPanel = new JPanel();
+        BoxLayout browserListBoxLayout = new BoxLayout(
+                browserListPanel,
+                BoxLayout.X_AXIS);
+        browserListPanel.setLayout(browserListBoxLayout);
+        browserListPanel.add(browserListLabel);
+        browserListPanel.add(Box.createHorizontalStrut(2));
+        browserListPanel.add(browserListField);
+        browserListPanel.add(Box.createHorizontalStrut(2));
+        browserListPanel.add(windowPolicyCBox);
+        browserListPanel.add(Box.createHorizontalStrut(2));
+        browserListPanel.add(prefBrowserBttn);
+
+        JPanel configPanel = new JPanel(new GridLayout(2, 1, 0, 2));
+        configPanel.add(browserListPanel);
+        configPanel.add(debugTextBttnPanel);
+
+        JPanel mainPanel = new JPanel(new BorderLayout());
+        mainPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 3));
+        mainPanel.add(debugTextScrollPane,
+                      java.awt.BorderLayout.CENTER);
+        mainPanel.add(urlPanel,
+                      java.awt.BorderLayout.NORTH);
+        mainPanel.add(configPanel,
+                      java.awt.BorderLayout.SOUTH);
+
+        this.getContentPane().add(mainPanel);
+        getRootPane().setDefaultButton(browseButton);
+        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+    }
+
+    private static void updateDebugTextArea(Exception exception,
+                                            JTextArea debugTextArea) {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter, true);
+        printWriter.println();
+        exception.printStackTrace(printWriter);
+        printWriter.println();
+        printWriter.close();
+        debugTextArea.append(stringWriter.toString());
+    }
+
+    private void browseButton_actionPerformed(ActionEvent e) {
+        if (logger.isInfoEnabled()) {
+            logger.info("browse button clicked");
+        }
+        try {
+            String urlString = urlTextField.getText();
+            if (urlString == null || urlString.trim().length() == 0) {
+                throw new MalformedURLException("You must specify a url.");
+            }
+            new URL(urlString); // may throw MalformedURLException
+            BrowserLauncherErrorHandler errorHandler = new TestAppErrorHandler(
+                    debugTextArea);
+            // use browser list if browserListField has data
+            String browserItems = browserListField.getText();
+            if (browserItems != null && browserItems.length() > 0) {
+                logger.debug("using browser list");
+                String[] browserArray = browserItems.split("[ ]+");
+                List browserList = Arrays.asList(browserArray);
+                logger.debug(browserList.toString());
+                launcher.openURLinBrowser(browserList,
+                                          urlString);
+            }
+            else {
+                String targetBrowser = browserBox.getSelectedItem().toString();
+                logger.debug(targetBrowser);
+                launcher.openURLinBrowser(targetBrowser,
+                                          urlString);
+            }
+        }
+        catch (Exception ex) {
+            // capture exception
+            BrowserLauncherTestApp.updateDebugTextArea(ex, debugTextArea);
+            // show message to user
+            JOptionPane.showMessageDialog(this,
+                                          ex.getMessage(),
+                                          "Error Message",
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+
+    private void copyButton_actionPerformed(ActionEvent e) {
+        if (logger.isInfoEnabled()) {
+            logger.info("copy button clicked");
+        }
+        debugTextArea.selectAll();
+        debugTextArea.copy();
+        debugTextArea.select(0, 0);
+    }
+
+    private void loggingLevelBttn_actionPerformed(ActionEvent e) {
+        String[] levels = logger.getLevelOptions();
+        int levelIndex = logger.getLevel();
+        String level = (String) JOptionPane.showInputDialog(
+                this,
+                bundle.getString("logging.level.select.message"),
+                bundle.getString("logging.level.select.title"),
+                JOptionPane.QUESTION_MESSAGE,
+                null,
+                levels,
+                levels[levelIndex]);
+        if (level != null && level.length() > 0) {
+            levelIndex = -1;
+            for (int idx = 0, max = levels.length;
+                                    idx < max && levelIndex == -1; idx++) {
+                if (level.equals(levels[idx])) {
+                    levelIndex = idx;
+                }
+
+            }
+            logger.setLevel(levelIndex);
+            loggingLevelTxtFld.setText(logger.getLevelText());
+        }
+    }
+
+    private static class TestAppErrorHandler
+            implements BrowserLauncherErrorHandler {
+        private JTextArea debugTextArea; // in ctor
+
+        TestAppErrorHandler(JTextArea debugTextArea) {
+            this.debugTextArea = debugTextArea;
+        }
+
+        public void handleException(Exception ex) {
+            // capture exception
+            BrowserLauncherTestApp.updateDebugTextArea(ex, debugTextArea);
+            // show message to user
+            JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
+                                          ex.getMessage(),
+                                          "Error Message",
+                                          JOptionPane.ERROR_MESSAGE);
+        }
+    }
+}
diff --git a/source/edu/stanford/ejalbert/testing/TestAppLogger.java b/source/edu/stanford/ejalbert/testing/TestAppLogger.java
new file mode 100644
index 0000000..d9a2344
--- /dev/null
+++ b/source/edu/stanford/ejalbert/testing/TestAppLogger.java
@@ -0,0 +1,107 @@
+/************************************************
+    Copyright 2005,2006 Jeff Chapman
+
+    This file is part of BrowserLauncher2.
+
+    BrowserLauncher2 is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    BrowserLauncher2 is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with BrowserLauncher2; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ ************************************************/
+// $Id: TestAppLogger.java,v 1.3 2006/03/23 20:55:14 jchapman0 Exp $
+package edu.stanford.ejalbert.testing;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.JTextArea;
+
+import net.sf.wraplog.AbstractLogger;
+import net.sf.wraplog.Level;
+
+/**
+ * Implements a logger for the test application. The log messages
+ * are sent to a JTextArea.
+ */
+class TestAppLogger
+        extends AbstractLogger {
+    private JTextArea debugTextArea; // in ctor
+    private String[] levelText; // in ctor
+    private SimpleDateFormat format; // in ctor =
+
+    public TestAppLogger(JTextArea debugTextArea,
+                         String[] levelLabels,
+                         String dateFormat) {
+        super();
+        this.debugTextArea = debugTextArea;
+        this.levelText = levelLabels;
+        this.format = new SimpleDateFormat(dateFormat);
+    }
+
+    /**
+     * Logs a message and optional error details.
+     *
+     * @param logLevel one of: Level.DEBUG, Level.INFO, Level.WARN,
+     *   Level.ERROR
+     * @param message the actual message; this will never be
+     *   <code>null</code>
+     * @param error an error that is related to the message; unless
+     *   <code>null</code>, the name and stack trace of the error are logged
+     * @throws Exception
+     * @todo Implement this net.sf.wraplog.AbstractLogger method
+     */
+    protected void reallyLog(int logLevel,
+                             String message,
+                             Throwable error)
+            throws Exception {
+        if (message == null) {
+            message = "null";
+        }
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter, true);
+        String threadName = Thread.currentThread().getName();
+        String dateAndTime = format.format(new Date());
+        printWriter.println(dateAndTime + " [" + threadName + "] "
+                            + getLevelText(logLevel) + " " + message);
+        if (error != null) {
+            error.printStackTrace(printWriter);
+        }
+        printWriter.println();
+        printWriter.close();
+        debugTextArea.append(stringWriter.toString());
+    }
+
+    public String getLevelText() {
+        return getLevelText(getLevel());
+    }
+
+    public String[] getLevelOptions() {
+        return levelText;
+    }
+
+    /**
+     * Return text that represents <code>logLevel</code>.
+     */
+    private String getLevelText(int logLevel) {
+        if (logLevel < Level.DEBUG || logLevel > Level.ERROR) {
+            throw new IllegalArgumentException(
+                    "logLevel must be one of those defined in net.sf.warplog.Level, but is "
+                    + Integer.toString(logLevel));
+        }
+        else {
+            return levelText[logLevel];
+        }
+    }
+}
diff --git a/source/net/sf/wraplog/AbstractLogger.java b/source/net/sf/wraplog/AbstractLogger.java
new file mode 100644
index 0000000..3788e0f
--- /dev/null
+++ b/source/net/sf/wraplog/AbstractLogger.java
@@ -0,0 +1,167 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+//
+// * Redistributions 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 the author nor the names of its contributors
+//may be used to endorse or promote products derived from this software
+//without specific prior written permission.
+//
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+//IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+//PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+//EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+//LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+/**
+ * Abstract base class to write messages about interesting things happening to a
+ * log.
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @author Thomas Aglassinger
+ */
+public abstract class AbstractLogger {
+
+    private int level = Level.DEBUG;
+
+    private int loggedMessageCount;
+
+    protected void checkLevel(int logLevel, String name) {
+        String actualName;
+        if (name == null) {
+            actualName = "level";
+        }
+        else {
+            actualName = name;
+        }
+        if ((logLevel < Level.DEBUG) || (logLevel > Level.ERROR)) {
+            throw new IllegalArgumentException(actualName
+                                               +
+                    " must be one of: Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR");
+        }
+    }
+
+    public void debug(String message) {
+        debug(message, null);
+    }
+
+    public void debug(String message, Throwable error) {
+        log(Level.DEBUG, message, error);
+    }
+
+    public void error(String message) {
+        error(message, null);
+    }
+
+    public void error(String message, Throwable error) {
+        log(Level.ERROR, message, error);
+    }
+
+    public int getLevel() {
+        return level;
+    }
+
+    /** Count of how many messages have been logged. */
+    public int getLoggedMessageCount() {
+        return loggedMessageCount;
+    }
+
+    public void info(String message) {
+        info(message, null);
+    }
+
+    public void info(String message, Throwable error) {
+        log(Level.INFO, message, error);
+    }
+
+    public boolean isEnabled(int logLevel) {
+        checkLevel(level, null);
+        return logLevel >= level;
+    }
+
+    /**
+     * Logs a message and optional error details.
+     *
+     * @param logLevel one of: Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR
+     * @param message the actual message; this will never be <code>null</code>
+     * @param error an error that is related to the message; unless <code>null</code>, the name and stack trace of the error are logged
+     */
+    protected abstract void reallyLog(int logLevel, String message,
+                                      Throwable error)
+            throws Exception;
+
+    /**
+     * Provided that <code>getLevel()</code> accepts it, log
+     * <code>message</code>. Otherwise, do nothing.
+     */
+    public void log(int logLevel, String message) {
+        log(logLevel, message, null);
+    }
+
+    /**
+     * Provided that <code>getLevel()</code> accepts it, log
+     * <code>message</code> and <code>error</code>. Otherwise, do nothing.
+     */
+    public void log(int logLevel, String message, Throwable error) {
+        if (isEnabled(logLevel)) {
+            try {
+                reallyLog(logLevel, message, error);
+                loggedMessageCount += 1;
+            }
+            catch (Exception error2) {
+                throw new LoggingException("cannot log message: " + message,
+                                           error2);
+            }
+        }
+    }
+
+    public void setLevel(int newLevel) {
+        if ((level >= Level.DEBUG) || (level <= Level.ERROR)) {
+            level = newLevel;
+        }
+        else {
+            throw new IllegalArgumentException(
+                    "newLevel must be one of: Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR");
+        }
+    }
+
+    public void warn(String message) {
+        warn(message, null);
+    }
+
+    public boolean isDebugEnabled() {
+        return isEnabled(Level.DEBUG);
+    }
+
+    public boolean isInfoEnabled() {
+        return isEnabled(Level.INFO);
+    }
+
+    public boolean isWarnEnabled() {
+        return isEnabled(Level.WARN);
+    }
+
+    public boolean isErrorEnabled() {
+        return isEnabled(Level.ERROR);
+    }
+
+    public void warn(String message, Throwable error) {
+        log(Level.WARN, message, error);
+    }
+}
diff --git a/source/net/sf/wraplog/Level.java b/source/net/sf/wraplog/Level.java
new file mode 100644
index 0000000..bfedbd4
--- /dev/null
+++ b/source/net/sf/wraplog/Level.java
@@ -0,0 +1,76 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+//
+// * Redistributions 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 the author nor the names of its contributors
+//may be used to endorse or promote products derived from this software
+//without specific prior written permission.
+//
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+//IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+//PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+//EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+//LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+/**
+ * Declaration of different logging levels.
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @author Thomas Aglassinger
+ */
+public class Level {
+    /** Logging level for messages that usually are of no interest for the user. */
+    public static final int DEBUG = 0;
+
+    /**
+     * Logging level for messages that explain why the desired operation cannot
+     * be performed.
+     * <p>
+     * Note: a simple way for deriving goog error messages is to make them fit
+     * the pattern: <blockquote>cannot do something: actual state must match
+     * expectation. </blockquote> Just fill in "do something", "actual state"
+     * and "expectation".<p> In case your code is just reporting a Java
+     * <code>Exception</code> that happens at a lower level on the call stack,
+     * use the pattern: <blockquote>cannot do something: <
+     * <code>exception.getMessage()</code> >. </blockquote> In practice,
+     * this often results into sucky error messages, but therer is not much you
+     * can do about it except making sure that if your own
+     * <code>Exceptions</code> have a useful <code>getMessage()</code>.
+     */
+    public static final int ERROR = 3;
+
+    /**
+     * Logging level for messages that tell details about normal operations
+     * currently going on.
+     */
+    public static final int INFO = 1;
+
+    /**
+     * Logging level for message that notify that the things can be processed,
+     * but the user might want to take a closer look at the current situation.
+     * <p>
+     * Note: warnings are a good indicator for bad design. They hint at the
+     * developer being to dumb to resolve a situation and therefor delegating
+     * the responsibility to the user. Preferrably, the code shoud be changed to
+     * either log <code>INFO</code>, throw an exception or log
+     * <code>ERROR</code>.
+     */
+    public static final int WARN = 2;
+}
diff --git a/source/net/sf/wraplog/Logger.java b/source/net/sf/wraplog/Logger.java
new file mode 100644
index 0000000..5d39487
--- /dev/null
+++ b/source/net/sf/wraplog/Logger.java
@@ -0,0 +1,70 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+ //
+ //* Redistributions 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 the author nor the names of its contributors
+   //may be used to endorse or promote products derived from this software
+   //without specific prior written permission.
+   //
+   //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+   //IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+   //THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   //PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   //EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   //PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   //PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   //LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+/**
+ * This class is not included in WrapLog version 1.1.
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @deprecated -- this class is no longer part of WrapLog.
+ * @version 1.0
+ */
+public class Logger
+        extends SystemLogger {
+    private static Logger logger;
+
+    private static synchronized Logger getLogger() {
+        if (logger == null) {
+            logger = new Logger();
+        }
+        return logger;
+    }
+
+    /**
+     * @deprecated
+     * @param name String
+     * @return Logger
+     */
+    public static Logger getLogger(String name) {
+        return getLogger();
+    }
+
+    /**
+     * @deprecated
+     * @param clazz Class
+     * @return Logger
+     */
+    public static Logger getLogger(Class clazz) {
+        if (clazz == null) {
+            throw new NullPointerException("parameter clazz must not be null");
+        }
+        return getLogger(clazz.getName());
+    }
+}
diff --git a/source/net/sf/wraplog/LoggingException.java b/source/net/sf/wraplog/LoggingException.java
new file mode 100644
index 0000000..4e51240
--- /dev/null
+++ b/source/net/sf/wraplog/LoggingException.java
@@ -0,0 +1,46 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+//
+// * Redistributions 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 the author nor the names of its contributors
+//may be used to endorse or promote products derived from this software
+//without specific prior written permission.
+//
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+//IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+//PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+//EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+//LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+/**
+ * RuntimeException to indicate that a message could not be logged.
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @author Thomas Aglassinger
+ */
+public class LoggingException
+        extends RuntimeException {
+    public LoggingException(String newMessage, Throwable cause) {
+        super(newMessage);
+        if (cause != null) {
+            initCause(cause);
+        }
+    }
+}
diff --git a/source/net/sf/wraplog/NoneLogger.java b/source/net/sf/wraplog/NoneLogger.java
new file mode 100644
index 0000000..cca5692
--- /dev/null
+++ b/source/net/sf/wraplog/NoneLogger.java
@@ -0,0 +1,52 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+//
+// * Redistributions 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 the author nor the names of its contributors
+//may be used to endorse or promote products derived from this software
+//without specific prior written permission.
+//
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+//IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+//PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+//EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+//LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+/**
+ * A Logger that does not log anywhere. This is a useful a an internal default
+ * for libraries before the client applications sets a logger.
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @author Thomas Aglassinger
+ */
+public class NoneLogger
+        extends AbstractLogger {
+
+    /**
+     * Does nothing. Messages are ignored.
+     *
+     * @see net.sf.wraplog.AbstractLogger#reallyLog(int, java.lang.String,
+     *           java.lang.Throwable)
+     */
+    protected void reallyLog(int logLevel, String message, Throwable error) {
+        // do nothing
+    }
+
+}
diff --git a/source/net/sf/wraplog/SystemLogger.java b/source/net/sf/wraplog/SystemLogger.java
new file mode 100644
index 0000000..6f8cc4c
--- /dev/null
+++ b/source/net/sf/wraplog/SystemLogger.java
@@ -0,0 +1,101 @@
+//Copyright (c) 2005, Thomas Aglassinger
+//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.
+//
+// * Redistributions 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 the author nor the names of its contributors
+//may be used to endorse or promote products derived from this software
+//without specific prior written permission.
+//
+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+//IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+//THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+//PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+//EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+//PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+//LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+package net.sf.wraplog;
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Logger to write messages to <code>System.out</code> (debug, info) and
+ * <code>System.err</code> (warn, error).
+ * <p>
+ * Updated to WrapLog version 1.1.
+ *
+ * @see System#err
+ * @see System#out
+ * @author Thomas Aglassinger
+ */
+//$Id: SystemLogger.java,v 1.3 2005/12/14 16:07:22 jchapman0 Exp $
+public class SystemLogger
+        extends AbstractLogger {
+
+    private SimpleDateFormat format = new SimpleDateFormat(
+            "yyyy-MM-dd HH:mm:ss,SSS");
+    /* (non-Javadoc)
+     * @see edu.stanford.ejalbert.logging.Logger#log(int, java.lang.String, java.lang.Throwable)
+     */
+    protected void reallyLog(int logLevel, String message, Throwable error) {
+        PrintStream stream;
+        if (logLevel < Level.WARN) {
+            stream = System.out;
+        }
+        else {
+            stream = System.err;
+        }
+        if (message == null) {
+            throw new NullPointerException("message must not be null");
+        }
+        if (stream == null) {
+            throw new NullPointerException("stream must not be null");
+        }
+        String threadName = Thread.currentThread().getName();
+        String dateAndTime = format.format(new Date());
+        stream.println(dateAndTime + " [" + threadName + "] "
+                       + getLevelText(logLevel) + " " + message);
+        if (error != null) {
+            error.printStackTrace(stream);
+        }
+    }
+
+    /**
+     * Return a text that represents <code>logLevel</code>.
+     */
+    protected String getLevelText(int logLevel) {
+        String result;
+        if (logLevel == Level.DEBUG) {
+            result = "DEBUG";
+        }
+        else if (logLevel == Level.INFO) {
+            result = "INFO ";
+        }
+        else if (logLevel == Level.WARN) {
+            result = "WARN ";
+        }
+        else if (logLevel == Level.ERROR) {
+            result = "ERROR";
+        }
+        else {
+            throw new IllegalArgumentException(
+                    "logLevel must be one of those defined in net.sf.warplog.Level, but is "
+                    + logLevel);
+        }
+        return result;
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/libbrowserlauncher-java.git



More information about the debian-med-commit mailing list