jSite: First commit : verion 4.0 (written by Bombe) 0.4
authorFlorent ‘nextgens’ Daignère <nextgens@freenetproject.org>
Mon, 3 Apr 2006 23:08:33 +0000 (23:08 +0000)
committerFlorent ‘nextgens’ Daignère <nextgens@freenetproject.org>
Mon, 3 Apr 2006 23:08:33 +0000 (23:08 +0000)
55 files changed:
COPYING [new file with mode: 0644]
build.xml [new file with mode: 0644]
images/jsite-icon.png [new file with mode: 0644]
src/de/todesbaum/jsite/application/EditionProject.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/FileOption.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/Freenet7Interface.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/InsertListener.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/Node.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/Project.java [new file with mode: 0644]
src/de/todesbaum/jsite/application/ProjectInserter.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/FileScanner.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/FileScannerListener.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/NodeManagerListener.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/NodeManagerPage.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/ProjectFilesPage.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/ProjectInsertPage.java [new file with mode: 0644]
src/de/todesbaum/jsite/gui/ProjectPage.java [new file with mode: 0644]
src/de/todesbaum/jsite/i18n/I18n.java [new file with mode: 0644]
src/de/todesbaum/jsite/i18n/jSite.properties [new file with mode: 0644]
src/de/todesbaum/jsite/i18n/jSite_de.properties [new file with mode: 0644]
src/de/todesbaum/jsite/main/CLI.java [new file with mode: 0644]
src/de/todesbaum/jsite/main/Configuration.java [new file with mode: 0644]
src/de/todesbaum/jsite/main/Main.java [new file with mode: 0644]
src/de/todesbaum/jsite/main/Version.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Client.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/ClientHello.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/ClientPut.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Command.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Connection.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/FileEntry.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Message.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Node.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Persistence.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/PriorityClass.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java [new file with mode: 0644]
src/de/todesbaum/util/freenet/fcp2/Verbosity.java [new file with mode: 0644]
src/de/todesbaum/util/image/IconLoader.java [new file with mode: 0644]
src/de/todesbaum/util/io/LineInputStream.java [new file with mode: 0644]
src/de/todesbaum/util/io/ReplacingOutputStream.java [new file with mode: 0644]
src/de/todesbaum/util/io/StreamCopier.java [new file with mode: 0644]
src/de/todesbaum/util/io/TempFileInputStream.java [new file with mode: 0644]
src/de/todesbaum/util/mime/DefaultMIMETypes.java [new file with mode: 0644]
src/de/todesbaum/util/swing/SortedListModel.java [new file with mode: 0644]
src/de/todesbaum/util/swing/TLabel.java [new file with mode: 0644]
src/de/todesbaum/util/swing/TWizard.java [new file with mode: 0644]
src/de/todesbaum/util/swing/TWizardPage.java [new file with mode: 0644]
src/de/todesbaum/util/swing/WizardListener.java [new file with mode: 0644]
src/de/todesbaum/util/xml/SimpleXML.java [new file with mode: 0644]
src/de/todesbaum/util/xml/XML.java [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) 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; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+  5. 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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+  7. 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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies 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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..f20fcb4
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0"?>
+
+<project name="jSite" default="compile">
+
+       <!-- create this file if you want to re-distribute jSite -->
+       <property file="build.properties" />
+       
+       <!-- default values if no build.properties is present -->
+       <property name="version" value="custom" />
+       <property name="javac.debug" value="false" />
+       
+       <property name="src.dir" value="${basedir}/src" />
+       <property name="bin.dir" value="${basedir}/build" />
+       <property name="dist.dir" value="${basedir}/dist" />
+       <property name="images.dir" value="${basedir}/images" />
+       
+       <!-- if the next lines fails, disable or delete it. you don't need it. unless you stole my development machine. -->
+       <available filepath="${basedir}/../todesbaum-lib" file="build.xml" property="maintainer-build" />
+       <available filepath="${basedir}/../todesbaum-lib" file="build.xml" property="todesbaum.src" value="${basedir}/../todesbaum-lib/src" />
+       
+       <!-- default values for non-maintainer builds -->
+       <property name="todesbaum.src" value="${src.dir}" />
+
+       <target name="prepare">
+               <mkdir dir="${bin.dir}" />
+       </target>
+
+       <target name="compile-maintainer" depends="prepare" if="maintainer-build">
+               <javac destdir="${bin.dir}" debug="false" optimize="true">
+                       <src path="${src.dir}" />
+                       <sourcepath path="${todesbaum.src}" />
+               </javac>
+       </target>
+       
+       <target name="compile-non-maintainer" depends="prepare" unless="maintainer-build">
+               <javac destdir="${bin.dir}" debug="false" optimize="true">
+                       <src path="${src.dir}" />
+                       <sourcepath path="${todesbaum.src}" />
+               </javac>
+       </target>
+       
+       <target name="compile" depends="compile-maintainer, compile-non-maintainer" description="compile jSite">
+               <copy todir="${bin.dir}">
+                       <fileset dir="${src.dir}">
+                               <include name="**/*.properties" />
+                       </fileset>
+                       <fileset dir="${images.dir}">
+                               <include name="**/*.png" />
+                       </fileset>
+               </copy>
+       </target>
+
+       <target name="clean" description="cleans the build environment">
+               <delete dir="${bin.dir}" />
+       </target>
+
+       <target name="dist-clean" depends="clean" description="cleans build and distribution environments">
+               <delete dir="${dist.dir}" />
+       </target>
+
+       <target name="prepare-dist">
+               <mkdir dir="${dist.dir}" />
+       </target>
+               
+       <target name="prepare-src-dist" depends="prepare-dist">
+               <mkdir dir="${dist.dir}/${ant.project.name}-${version}" />
+               <mkdir dir="${dist.dir}/${ant.project.name}-${version}/src" />
+               <mkdir dir="${dist.dir}/${ant.project.name}-${version}/images" />
+       </target>
+
+       <target name="jar" depends="compile, prepare-dist" description="creates jSite jar file">
+               <jar destfile="${dist.dir}/${ant.project.name}.jar">
+                       <fileset dir="${bin.dir}" />
+                       <manifest>
+                               <attribute name="Main-Class" value="de.todesbaum.jsite.main.Main"/>
+                       </manifest>
+               </jar>
+       </target>
+
+       <target name="dist-bin-zip" depends="jar" description="create binary distribution zip">
+               <zip destfile="${dist.dir}/${ant.project.name}-${version}-bin.zip">
+                       <fileset dir="${dist.dir}">
+                               <include name="${ant.project.name}.jar" />
+                       </fileset>
+                       <fileset dir="${basedir}">
+                               <include name="COPYING" />
+                       </fileset>
+               </zip>
+       </target>
+       
+       <target name="dist-bin-tbz2" depends="jar" description="create binary distribution tar.bz2">
+               <tar destfile="${dist.dir}/${ant.project.name}-${version}-bin.tar.bz2" compression="bzip2">
+                       <tarfileset dir="${dist.dir}">
+                               <include name="${ant.project.name}.jar" />
+                       </tarfileset>
+                       <tarfileset dir="${basedir}">
+                               <include name="COPYING" />
+                       </tarfileset>
+               </tar>
+       </target>
+       
+       <target name="dist-bin" depends="dist-bin-zip, dist-bin-tbz2"/>
+       
+       <target name="collect-maintainer-src" depends="prepare-src-dist" if="maintainer-build">
+               <copy todir="${dist.dir}/${ant.project.name}-${version}/src">
+                       <fileset dir="${todesbaum.src}">
+                               <includesfile name="build.collect-src.files" />
+                       </fileset>
+               </copy>
+       </target>
+       
+       <target name="collect-src" depends="prepare-src-dist, collect-maintainer-src">
+               <copy todir="${dist.dir}/${ant.project.name}-${version}/src">
+                       <fileset dir="${src.dir}">
+                               <include name="**/*.java" />
+                               <include name="**/*.properties" />
+                       </fileset>
+               </copy>
+               <copy todir="${dist.dir}/${ant.project.name}-${version}/images">
+                       <fileset dir="${images.dir}">
+                               <include name="**/*.png" />
+                       </fileset>
+               </copy>
+               <copy todir="${dist.dir}/${ant.project.name}-${version}">
+                       <fileset dir="${basedir}">
+                               <include name="COPYING" />
+                               <include name="build.xml" />
+                       </fileset>
+               </copy>
+       </target>
+       
+       <target name="dist-src" depends="dist-src-zip, dist-src-tbz2" />
+       
+       <target name="dist-src-zip" depends="collect-src" description="create source distribution zip">
+               <zip destfile="${dist.dir}/${ant.project.name}-${version}-src.zip">
+                       <fileset dir="${dist.dir}">
+                               <include name="${ant.project.name}-${version}/**/*" />
+                       </fileset>
+               </zip>
+       </target>
+
+       <target name="dist-src-tbz2" depends="collect-src" description="create source distribution tar.bz2">
+               <tar destfile="${dist.dir}/${ant.project.name}-${version}-src.tar.bz2" compression="bzip2">
+                       <tarfileset dir="${dist.dir}">
+                               <include name="${ant.project.name}-${version}/**/*" />
+                       </tarfileset>
+               </tar>
+       </target>
+       
+       <target name="dist" depends="dist-bin, dist-src" description="creates all distribution files"/>
+       
+       <target name="build-collect-src" depends="clean, compile" if="maintainer-build">
+               <exec executable="/bin/sh" output="build.collect-src.files">
+                       <arg value="-c"/>
+                       <arg value="find build -type f | grep -v '\$' | sed 's,^build/,,' | grep '^de/todesbaum/util/' | sed 's,class$,java,'" />
+               </exec>
+       </target>
+
+</project>
+
diff --git a/images/jsite-icon.png b/images/jsite-icon.png
new file mode 100644 (file)
index 0000000..fcddf6c
Binary files /dev/null and b/images/jsite-icon.png differ
diff --git a/src/de/todesbaum/jsite/application/EditionProject.java b/src/de/todesbaum/jsite/application/EditionProject.java
new file mode 100644 (file)
index 0000000..e548115
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+/**
+ * Project extension for edition-based projects. In Freenet 0.7 this is
+ * currently the only project type.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: EditionProject.java 417 2006-03-29 12:36:54Z bombe $
+ */
+public class EditionProject extends Project {
+
+       /** The edition to insert to. */
+       private int edition;
+
+       /**
+        * Creates a new edition-based project.
+        */
+       public EditionProject() {
+       }
+
+       /**
+        * Clones the specified project as an edition-based project.
+        * 
+        * @param project
+        *            The project to clone
+        */
+       public EditionProject(Project project) {
+               super(project);
+               if (project instanceof EditionProject) {
+                       edition = ((EditionProject) project).edition;
+               }
+       }
+
+       /**
+        * Returns the edition of the project.
+        * 
+        * @return The edition of the project
+        */
+       public int getEdition() {
+               return edition;
+       }
+
+       /**
+        * Sets the edition of the project.
+        * 
+        * @param edition
+        *            The edition to set
+        */
+       public void setEdition(int edition) {
+               this.edition = edition;
+       }
+
+       /**
+        * Constructs the final request URI including the edition number.
+        * 
+        * @return The final request URI
+        */
+       @Override
+       public String getFinalURI(int editionOffset) {
+               return requestURI + path + "-" + (edition + editionOffset) + "/";
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/application/FileOption.java b/src/de/todesbaum/jsite/application/FileOption.java
new file mode 100644 (file)
index 0000000..6b1ed81
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+public class FileOption {
+
+       private static final boolean DEFAULT_INSERT = true;
+       private static final String DEFAULT_CUSTOM_KEY = "CHK@";
+       private static final String DEFAULT_CONTAINER = "";
+       private static final int DEFAULT_EDITION_RANGE = 3;
+       private static final boolean DEFAULT_REPLACE_EDITION = false;
+
+       private boolean insert;
+       private String customKey;
+       private final String defaultMimeType;
+       private String mimeType;
+       private String container;
+       private int editionRange;
+       private boolean replaceEdition;
+
+       public FileOption(String defaultMimeType) {
+               insert = DEFAULT_INSERT;
+               customKey = DEFAULT_CUSTOM_KEY;
+               this.defaultMimeType = defaultMimeType;
+               this.mimeType = defaultMimeType;
+               this.container = DEFAULT_CONTAINER;
+               this.editionRange = DEFAULT_EDITION_RANGE;
+               this.replaceEdition = DEFAULT_REPLACE_EDITION;
+       }
+
+       /**
+        * @return Returns the customKey.
+        */
+       public String getCustomKey() {
+               return customKey;
+       }
+
+       /**
+        * @param customKey
+        *            The customKey to set.
+        */
+       public void setCustomKey(String customKey) {
+               if (customKey == null) {
+                       customKey = "";
+               }
+               this.customKey = customKey;
+       }
+
+       /**
+        * @return Returns the insert.
+        */
+       public boolean isInsert() {
+               return insert;
+       }
+
+       /**
+        * @param insert
+        *            The insert to set.
+        */
+       public void setInsert(boolean insert) {
+               this.insert = insert;
+       }
+
+       public void setMimeType(String mimeType) {
+               if (mimeType == null) {
+                       mimeType = defaultMimeType;
+               }
+               this.mimeType = mimeType;
+       }
+
+       public String getMimeType() {
+               return mimeType;
+       }
+
+       /**
+        * @return Returns the container.
+        */
+       public String getContainer() {
+               return container;
+       }
+
+       /**
+        * @param container
+        *            The container to set.
+        */
+       public void setContainer(String container) {
+               if (container == null) {
+                       container = DEFAULT_CONTAINER;
+               }
+               this.container = container;
+       }
+
+       public void setReplaceEdition(boolean replaceEdition) {
+               this.replaceEdition = replaceEdition;
+       }
+
+       public boolean getReplaceEdition() {
+               return replaceEdition;
+       }
+
+       public void setEditionRange(int editionRange) {
+               this.editionRange = editionRange;
+       }
+
+       public int getEditionRange() {
+               return editionRange;
+       }
+
+       public boolean isCustom() {
+               if (insert != DEFAULT_INSERT)
+                       return true;
+               if (!customKey.equals(DEFAULT_CUSTOM_KEY))
+                       return true;
+               if (!defaultMimeType.equals(mimeType))
+                       return true;
+               if (!DEFAULT_CONTAINER.equals(container))
+                       return true;
+               if (replaceEdition != DEFAULT_REPLACE_EDITION)
+                       return true;
+               if (editionRange != DEFAULT_EDITION_RANGE)
+                       return true;
+               return false;
+       }
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/jsite/application/Freenet7Interface.java b/src/de/todesbaum/jsite/application/Freenet7Interface.java
new file mode 100644 (file)
index 0000000..fefb220
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * jSite - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+import java.io.IOException;
+
+import de.todesbaum.util.freenet.fcp2.Client;
+import de.todesbaum.util.freenet.fcp2.Connection;
+import de.todesbaum.util.freenet.fcp2.GenerateSSK;
+import de.todesbaum.util.freenet.fcp2.Message;
+import de.todesbaum.util.freenet.fcp2.Node;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Freenet7Interface.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public class Freenet7Interface {
+
+       private static int counter = 0;
+
+       private Node node;
+       private Connection connection;
+
+       public void setNodeAddress(String hostname) {
+               node = new Node(hostname);
+               connection = new Connection(node, "connection-" + counter++);
+       }
+
+       public void setNodeAddress(String hostname, int port) {
+               node = new Node(hostname, port);
+               connection = new Connection(node, "connection-" + counter++);
+       }
+       
+       public void setNode(de.todesbaum.jsite.application.Node node) {
+               if (node != null) {
+                       this.node = new Node(node.getHostname(), node.getPort());
+                       connection = new Connection(node, "connection-" + counter++);
+               } else {
+                       this.node = null;
+                       connection = null;
+               }
+       }
+       
+       public void removeNode() {
+               node = null;
+               connection = null;
+       }
+
+       /**
+        * @return Returns the node.
+        */
+       public Node getNode() {
+               return node;
+       }
+
+       /**
+        * @return Returns the connection.
+        */
+       public Connection getConnection(String identifier) {
+               return new Connection(node, identifier);
+       }
+
+       public boolean isNodePresent() throws IOException {
+               if (!connection.isConnected()) {
+                       return connection.connect();
+               }
+               return true;
+       }
+
+       public String[] generateKeyPair() throws IOException {
+               if (!isNodePresent()) {
+                       return null;
+               }
+               GenerateSSK generateSSK = new GenerateSSK();
+               Client client = new Client(connection, generateSSK);
+               Message keypairMessage = client.readMessage();
+               return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") };
+       }
+
+       /**
+        * @return <code>true</code> if this interface already has a node set,
+        *         <code>false</code> otherwise
+        */
+       public boolean hasNode() {
+               return (node != null) && (connection != null);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/application/InsertListener.java b/src/de/todesbaum/jsite/application/InsertListener.java
new file mode 100644 (file)
index 0000000..0966e10
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+import java.util.EventListener;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: InsertListener.java 397 2006-03-25 16:11:34Z bombe $
+ */
+public interface InsertListener extends EventListener {
+
+       public static enum ErrorType {
+               KEY_COLLISION, ROUTE_NOT_FOUND, DATA_NOT_FOUND, FCP_ERROR, IO_ERROR
+       }
+
+       public void projectInsertStarted(Project project);
+
+       public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized);
+
+       public void projectInsertFinished(Project project, boolean success, Throwable cause);
+
+}
diff --git a/src/de/todesbaum/jsite/application/Node.java b/src/de/todesbaum/jsite/application/Node.java
new file mode 100644 (file)
index 0000000..7db0cb0
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * jSite-0.7 - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Node.java 419 2006-03-29 17:49:46Z bombe $
+ */
+public class Node extends de.todesbaum.util.freenet.fcp2.Node {
+
+       protected String name;
+
+       /**
+        * @param hostname
+        */
+       public Node(String hostname) {
+               this(hostname, DEFAULT_PORT);
+       }
+
+       /**
+        * @param hostname
+        * @param port
+        */
+       public Node(String hostname, int port) {
+               this(hostname, port, "");
+       }
+
+       public Node(String hostname, int port, String name) {
+               super(hostname, port);
+               this.name = name;
+       }
+
+       public Node(Node node) {
+               this(node.getHostname(), node.getPort());
+       }
+
+       public Node(Node node, String name) {
+               this(node.getHostname(), node.getPort(), name);
+       }
+
+       /**
+        * @param name
+        *            The name to set.
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       /**
+        * @return Returns the name.
+        */
+       public String getName() {
+               return name;
+       }
+
+       public void setHostname(String hostname) {
+               this.hostname = hostname;
+       }
+
+       public void setPort(int port) {
+               this.port = port;
+       }
+       
+       public boolean equals(Object o) {
+               if ((o == null) || !(o instanceof Node)) {
+                       return false;
+               }
+               Node node = (Node) o;
+               return name.equals(node.name) && hostname.equals(node.hostname) && (port == node.port);
+       }
+       
+       public int hashCode() {
+               return name.hashCode() ^ hostname.hashCode() ^ port;
+       }
+
+       public String toString() {
+               return name + " (" + hostname + ":" + port + ")";
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/application/Project.java b/src/de/todesbaum/jsite/application/Project.java
new file mode 100644 (file)
index 0000000..0f28ef3
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import de.todesbaum.util.mime.DefaultMIMETypes;
+
+/**
+ * @author David Roden <dr@todesbaum.dyndns.org>
+ * @version $Id: Project.java 357 2006-03-24 15:46:03Z bombe $
+ */
+public abstract class Project implements Comparable {
+
+       protected String name;
+       protected String description;
+
+       protected String insertURI;
+       protected String requestURI;
+
+       protected String indexFile;
+       protected String localPath;
+       protected String path;
+       protected long lastInsertionTime;
+
+       protected Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
+
+       public Project() {
+       }
+
+       /**
+        * Clone-constructor.
+        * 
+        * @param project
+        */
+       public Project(Project project) {
+               name = project.name;
+               description = project.description;
+               insertURI = project.insertURI;
+               requestURI = project.requestURI;
+               path = project.path;
+               localPath = project.localPath;
+               indexFile = project.indexFile;
+               lastInsertionTime = project.lastInsertionTime;
+               fileOptions = new HashMap<String, FileOption>(project.fileOptions);
+       }
+
+       /**
+        * @return Returns the title.
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * @param title
+        *            The title to set.
+        */
+       public void setName(String title) {
+               this.name = title;
+       }
+
+       /**
+        * @return Returns the description.
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * @param description
+        *            The description to set.
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+       /**
+        * @return Returns the localPath.
+        */
+       public String getLocalPath() {
+               return localPath;
+       }
+
+       /**
+        * @param localPath
+        *            The localPath to set.
+        */
+       public void setLocalPath(String localPath) {
+               this.localPath = localPath;
+       }
+
+       /**
+        * @return Returns the indexFile.
+        */
+       public String getIndexFile() {
+               return indexFile;
+       }
+
+       /**
+        * @param indexFile
+        *            The indexFile to set.
+        */
+       public void setIndexFile(String indexFile) {
+               this.indexFile = indexFile;
+       }
+
+       /**
+        * @return Returns the lastInserted.
+        */
+       public long getLastInsertionTime() {
+               return lastInsertionTime;
+       }
+
+       /**
+        * @param lastInserted
+        *            The lastInserted to set.
+        */
+       public void setLastInsertionTime(long lastInserted) {
+               this.lastInsertionTime = lastInserted;
+       }
+
+       /**
+        * @return Returns the name.
+        */
+       public String getPath() {
+               return path;
+       }
+
+       /**
+        * @param name
+        *            The name to set.
+        */
+       public void setPath(String name) {
+               this.path = name;
+       }
+
+       /**
+        * @return Returns the insertURI.
+        */
+       public String getInsertURI() {
+               return insertURI;
+       }
+
+       /**
+        * @param insertURI
+        *            The insertURI to set.
+        */
+       public void setInsertURI(String insertURI) {
+               this.insertURI = insertURI;
+       }
+
+       /**
+        * @return Returns the requestURI.
+        */
+       public String getRequestURI() {
+               return requestURI;
+       }
+
+       /**
+        * @param requestURI
+        *            The requestURI to set.
+        */
+       public void setRequestURI(String requestURI) {
+               this.requestURI = requestURI;
+       }
+
+       public String toString() {
+               return name;
+       }
+
+       public String shortenFilename(File file) {
+               String filename = file.getPath();
+               if (filename.startsWith(localPath)) {
+                       filename = filename.substring(localPath.length());
+                       if (filename.startsWith(File.separator)) {
+                               filename = filename.substring(1);
+                       }
+               }
+               return filename;
+       }
+
+       public FileOption getFileOption(String filename) {
+               FileOption fileOption = fileOptions.get(filename);
+               if (fileOption == null) {
+                       fileOption = new FileOption(DefaultMIMETypes.guessMIMEType(filename));
+                       fileOptions.put(filename, fileOption);
+               }
+               return fileOption;
+       }
+
+       public void setFileOption(String filename, FileOption fileOption) {
+               fileOptions.put(filename, fileOption);
+       }
+
+       /**
+        * @return Returns the fileOptions.
+        */
+       public Map<String, FileOption> getFileOptions() {
+               return Collections.unmodifiableMap(fileOptions);
+       }
+
+       /**
+        * @param fileOptions
+        *            The fileOptions to set.
+        */
+       public void setFileOptions(Map<String, FileOption> fileOptions) {
+               this.fileOptions.clear();
+               this.fileOptions.putAll(fileOptions);
+       }
+
+       public String getFinalURI(int editionOffset) {
+               return requestURI + path + "/";
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int compareTo(Object o) {
+               return name.compareToIgnoreCase(((Project) o).name);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/application/ProjectInserter.java b/src/de/todesbaum/jsite/application/ProjectInserter.java
new file mode 100644 (file)
index 0000000..8ec92db
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.application;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import de.todesbaum.jsite.gui.FileScanner;
+import de.todesbaum.jsite.gui.FileScannerListener;
+import de.todesbaum.util.freenet.fcp2.Client;
+import de.todesbaum.util.freenet.fcp2.ClientPutComplexDir;
+import de.todesbaum.util.freenet.fcp2.Connection;
+import de.todesbaum.util.freenet.fcp2.DirectFileEntry;
+import de.todesbaum.util.freenet.fcp2.FileEntry;
+import de.todesbaum.util.freenet.fcp2.Message;
+import de.todesbaum.util.freenet.fcp2.RedirectFileEntry;
+import de.todesbaum.util.freenet.fcp2.Verbosity;
+import de.todesbaum.util.io.ReplacingOutputStream;
+import de.todesbaum.util.io.StreamCopier;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ProjectInserter.java 440 2006-03-30 09:31:25Z bombe $
+ */
+public class ProjectInserter implements FileScannerListener, Runnable {
+
+       private static int counter = 0;
+       private boolean debug = false;
+       private List<InsertListener> insertListeners = new ArrayList<InsertListener>();
+       protected Freenet7Interface freenetInterface;
+       protected Project project;
+       private FileScanner fileScanner;
+       protected final Object lockObject = new Object();
+       private int maxRetries = 99999;
+
+       public void addInsertListener(InsertListener insertListener) {
+               insertListeners.add(insertListener);
+       }
+
+       public void removeInsertListener(InsertListener insertListener) {
+               insertListeners.remove(insertListener);
+       }
+
+       protected void fireProjectInsertStarted() {
+               for (InsertListener insertListener: insertListeners) {
+                       insertListener.projectInsertStarted(project);
+               }
+       }
+
+       protected void fireProjectInsertProgress(int succeeded, int failed, int fatal, int total, boolean finalized) {
+               for (InsertListener insertListener: insertListeners) {
+                       insertListener.projectInsertProgress(project, succeeded, failed, fatal, total, finalized);
+               }
+       }
+
+       protected void fireProjectInsertFinished(boolean success, Throwable cause) {
+               for (InsertListener insertListener: insertListeners) {
+                       insertListener.projectInsertFinished(project, success, cause);
+               }
+       }
+
+       /**
+        * @param debug
+        *            The debug to set.
+        */
+       public void setDebug(boolean debug) {
+               this.debug = debug;
+       }
+
+       /**
+        * @param project
+        *            The project to set.
+        */
+       public void setProject(Project project) {
+               this.project = project;
+       }
+
+       /**
+        * @param freenetInterface
+        *            The freenetInterface to set.
+        */
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       /**
+        * @param maxRetries
+        *            The maxRetries to set.
+        */
+       public void setMaxRetries(int maxRetries) {
+               this.maxRetries = maxRetries;
+       }
+
+       public void start() {
+               fileScanner = new FileScanner(project);
+               fileScanner.addFileScannerListener(this);
+               new Thread(fileScanner).start();
+       }
+
+       private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException {
+               File file = new File(project.getLocalPath(), filename);
+               length[0] = file.length();
+               if (!fileOption.getReplaceEdition()) {
+                       return new FileInputStream(file);
+               }
+               ByteArrayOutputStream filteredByteOutputStream = new ByteArrayOutputStream(Math.min(Integer.MAX_VALUE, (int) length[0]));
+               ReplacingOutputStream outputStream = new ReplacingOutputStream(filteredByteOutputStream);
+               FileInputStream fileInput = new FileInputStream(file);
+               outputStream.addReplacement("$[CONTAINER]", "/");
+               outputStream.addReplacement("$[EDITION]", String.valueOf(edition));
+               outputStream.addReplacement("$[URI]", project.getFinalURI(0));
+               for (int index = 1; index <= fileOption.getEditionRange(); index++) {
+               outputStream.addReplacement("$[URI+" + index + "]", project.getFinalURI(index));
+                       outputStream.addReplacement("$[URI+" + index + "]", project.getFinalURI(index));
+               }
+               StreamCopier.copy(fileInput, outputStream, length[0]);
+               outputStream.close();
+               filteredByteOutputStream.close();
+               byte[] filteredBytes = filteredByteOutputStream.toByteArray();
+               length[0] = filteredBytes.length;
+               return new ByteArrayInputStream(filteredBytes);
+       }
+
+       private InputStream createContainerInputStream(Map<String, List<String>> containerFiles, String containerName, int edition, long[] containerLength) throws IOException {
+               File tempFile = File.createTempFile("jsite", ".zip");
+               tempFile.deleteOnExit();
+               FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
+               ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
+               for (String filename: containerFiles.get(containerName)) {
+                       File dataFile = new File(project.getLocalPath(), filename);
+                       if (dataFile.exists()) {
+                               ZipEntry zipEntry = new ZipEntry(filename);
+                               long[] fileLength = new long[1];
+                               InputStream wrappedInputStream = createFileInputStream(filename, project.getFileOption(filename), edition, fileLength);
+                               zipOutputStream.putNextEntry(zipEntry);
+                               StreamCopier.copy(wrappedInputStream, zipOutputStream, fileLength[0]);
+                               zipOutputStream.closeEntry();
+                               wrappedInputStream.close();
+                       }
+               }
+               zipOutputStream.closeEntry();
+
+               /* FIXME - create metadata */
+               // ZipEntry metadataEntry = new ZipEntry("metadata");
+               // zipOutputStream.putNextEntry(metadataEntry);
+               // Metadata zipMetadata = new Metadata();
+               // for (String filename: containerFiles.get(containerName)) {
+               // if (new File(project.getLocalPath(), filename).exists()) {
+               // DocumentMetadata zipEntryMetadata = new DocumentMetadata();
+               // zipEntryMetadata.setName(filename);
+               // zipEntryMetadata.setFormat(project.getFileOption(filename).getMimeType());
+               // zipMetadata.addDocument(zipEntryMetadata);
+               // }
+               // }
+               // zipOutputStream.write(zipMetadata.toByteArray());
+               // zipOutputStream.closeEntry();
+               zipOutputStream.close();
+
+               containerLength[0] = tempFile.length();
+               return new FileInputStream(tempFile);
+       }
+
+       private FileEntry createFileEntry(String filename, int edition, Map<String, List<String>> containerFiles) {
+               FileEntry fileEntry = null;
+               FileOption fileOption = project.getFileOption(filename);
+               if (filename.startsWith("/container/:")) {
+                       String containerName = filename.substring("/container/:".length());
+                       try {
+                               long[] containerLength = new long[1];
+                               InputStream containerInputStream = createContainerInputStream(containerFiles, containerName, edition, containerLength);
+                               fileEntry = new DirectFileEntry(containerName + ".zip", "application/zip", containerInputStream, containerLength[0]);
+                       } catch (IOException ioe1) {
+                       }
+               } else {
+                       if (fileOption.isInsert()) {
+                               try {
+                                       long[] fileLength = new long[1];
+                                       InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength);
+                                       fileEntry = new DirectFileEntry(filename, project.getFileOption(filename).getMimeType(), fileEntryInputStream, fileLength[0]);
+                               } catch (IOException ioe1) {
+                               }
+                       } else {
+                               fileEntry = new RedirectFileEntry(filename, fileOption.getMimeType(), fileOption.getCustomKey());
+                       }
+               }
+               return fileEntry;
+       }
+
+       private void createContainers(List<String> files, List<String> containers, Map<String, List<String>> containerFiles) {
+               for (String filename: new ArrayList<String>(files)) {
+                       FileOption fileOption = project.getFileOption(filename);
+                       String containerName = fileOption.getContainer();
+                       if (!containerName.equals("")) {
+                               if (!containers.contains(containerName)) {
+                                       containers.add(containerName);
+                                       containerFiles.put(containerName, new ArrayList<String>());
+                                       /* hmm. looks like a hack to me. */
+                                       files.add("/container/:" + containerName);
+                               }
+                               containerFiles.get(containerName).add(filename);
+                               files.remove(filename);
+                       }
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void run() {
+               fireProjectInsertStarted();
+               List<String> files = fileScanner.getFiles();
+
+               /* create connection to node */
+               Connection connection = freenetInterface.getConnection("project-insert-" + counter++);
+               try {
+                       connection.connect();
+               } catch (IOException e1) {
+                       fireProjectInsertFinished(false, e1);
+                       return;
+               }
+               Client client = new Client(connection);
+
+               /* create containers */
+               final List<String> containers = new ArrayList<String>();
+               final Map<String, List<String>> containerFiles = new HashMap<String, List<String>>();
+               createContainers(files, containers, containerFiles);
+
+               /* collect files */
+               int edition = ((EditionProject) project).getEdition();
+               String dirURI = project.getInsertURI() + project.getPath() + "-" + edition;
+               ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI);
+               putDir.setDefaultName(project.getIndexFile());
+               putDir.setVerbosity(Verbosity.ALL);
+               putDir.setMaxRetries(maxRetries);
+               for (String filename: files) {
+                       FileEntry fileEntry = createFileEntry(filename, edition, containerFiles);
+                       if (fileEntry != null) {
+                               putDir.addFileEntry(fileEntry);
+                       }
+               }
+
+               /* start request */
+               try {
+                       client.execute(putDir);
+               } catch (IOException ioe1) {
+                       fireProjectInsertFinished(false, ioe1);
+                       return;
+               }
+
+               /* parse progress and success messages */
+               boolean success = true;
+               boolean finished = false;
+               boolean disconnected = false;
+               while (!finished) {
+                       Message message = client.readMessage();
+                       finished = (message == null) && (disconnected = client.isDisconnected());
+                       if (debug) {
+                               System.out.println(message);
+                       }
+                       if (!finished) {
+                               String messageName = message.getName();
+                               if ("SimpleProgress".equals(messageName)) {
+                                       int total = Integer.parseInt(message.get("Total"));
+                                       int succeeded = Integer.parseInt(message.get("Succeeded"));
+                                       int fatal = Integer.parseInt(message.get("FatallyFailed"));
+                                       int failed = Integer.parseInt(message.get("Failed"));
+                                       boolean finalized = Boolean.parseBoolean(message.get("FinalizedTotal"));
+                                       fireProjectInsertProgress(succeeded, failed, fatal, total, finalized);
+                               }
+                               success = "PutSuccessful".equals(messageName);
+                               finished = success || "PutFailed".equals(messageName);
+                       }
+               }
+
+               /* post-insert work */
+               fireProjectInsertFinished(success, disconnected ? new IOException("Connection terminated") : null);
+               if (success) {
+                       if (project instanceof EditionProject) {
+                               ((EditionProject) project).setEdition(edition + 1);
+                       }
+               }
+       }
+
+       //
+       // INTERFACE FileScannerListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void fileScannerFinished(FileScanner fileScanner) {
+               if (!fileScanner.isError()) {
+                       new Thread(this).start();
+               } else {
+                       fireProjectInsertFinished(false, null);
+               }
+               fileScanner.removeFileScannerListener(this);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/gui/FileScanner.java b/src/de/todesbaum/jsite/gui/FileScanner.java
new file mode 100644 (file)
index 0000000..1b63fcd
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.i18n.I18n;
+
+public class FileScanner implements Runnable {
+
+       private final List<FileScannerListener> fileScannerListeners = new ArrayList<FileScannerListener>();
+       private final Project project;
+       private List<String> files;
+       private boolean error = false;
+
+       public FileScanner(Project project) {
+               this.project = project;
+       }
+
+       public void addFileScannerListener(FileScannerListener fileScannerListener) {
+               fileScannerListeners.add(fileScannerListener);
+       }
+
+       public void removeFileScannerListener(FileScannerListener fileScannerListener) {
+               fileScannerListeners.remove(fileScannerListener);
+       }
+
+       protected void fireFileScannerFinished() {
+               for (FileScannerListener fileScannerListener: new ArrayList<FileScannerListener>(fileScannerListeners)) {
+                       fileScannerListener.fileScannerFinished(this);
+               }
+       }
+
+       public void run() {
+               files = new ArrayList<String>();
+               error = false;
+               try {
+                       scanFiles(new File(project.getLocalPath()), files);
+                       Collections.sort(files);
+               } catch (IOException ioe1) {
+                       error = true;
+               }
+               fireFileScannerFinished();
+       }
+
+       public boolean isError() {
+               return error;
+       }
+
+       public List<String> getFiles() {
+               return files;
+       }
+
+       private void scanFiles(File rootDir, List<String> fileList) throws IOException {
+               File[] files = rootDir.listFiles(new FileFilter() {
+
+                       public boolean accept(File file) {
+                               return !file.isHidden();
+                       }
+               });
+               if (files == null) {
+                       throw new IOException(I18n.getMessage("jsite.file-scanner.can-not-read-directory"));
+               }
+               for (File file: files) {
+                       if (file.isDirectory()) {
+                               scanFiles(file, fileList);
+                               continue;
+                       }
+                       String filename = project.shortenFilename(file);
+                       fileList.add(filename);
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/jsite/gui/FileScannerListener.java b/src/de/todesbaum/jsite/gui/FileScannerListener.java
new file mode 100644 (file)
index 0000000..b8e373b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.util.EventListener;
+
+public interface FileScannerListener extends EventListener {
+
+       public void fileScannerFinished(FileScanner fileScanner);
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/jsite/gui/NodeManagerListener.java b/src/de/todesbaum/jsite/gui/NodeManagerListener.java
new file mode 100644 (file)
index 0000000..039561e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * jSite-0.7 - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.util.EventListener;
+
+import de.todesbaum.jsite.application.Node;
+
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: NodeManagerListener.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public interface NodeManagerListener extends EventListener {
+
+       public void nodesUpdated(Node[] nodes);
+       
+}
diff --git a/src/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/de/todesbaum/jsite/gui/NodeManagerPage.java
new file mode 100644 (file)
index 0000000..9b1c80a
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * jSite-0.7 - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: NodeManagerPage.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public class NodeManagerPage extends TWizardPage implements ListSelectionListener, DocumentListener, ChangeListener {
+
+       private List<NodeManagerListener> nodeManagerListeners = new ArrayList<NodeManagerListener>();
+       private TWizard wizard;
+
+       private Action addNodeAction;
+       private Action deleteNodeAction;
+       private DefaultListModel nodeListModel;
+       private JList nodeList;
+       private JTextField nodeNameTextField;
+       private JTextField nodeHostnameTextField;
+       private JSpinner nodePortSpinner;
+
+       public NodeManagerPage() {
+               super();
+               pageInit();
+               setHeading(I18n.getMessage("jsite.node-manager.heading"));
+               setDescription(I18n.getMessage("jsite.node-manager.description"));
+       }
+       
+       public void addNodeManagerListener(NodeManagerListener nodeManagerListener) {
+               nodeManagerListeners.add(nodeManagerListener);
+       }
+       
+       public void removeNodeManagerListener(NodeManagerListener nodeManagerListener) {
+               nodeManagerListeners.remove(nodeManagerListener);
+       }
+       
+       protected void fireNodesUpdated(Node[] nodes) {
+               for (NodeManagerListener nodeManagerListener: nodeManagerListeners) {
+                       nodeManagerListener.nodesUpdated(nodes);
+               }
+       }
+
+       private void createActions() {
+               addNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.add-node")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               addNode();
+                       }
+               };
+
+               deleteNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.delete-node")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               deleteNode();
+                       }
+               };
+               deleteNodeAction.setEnabled(false);
+       }
+
+       private void pageInit() {
+               createActions();
+               nodeListModel = new DefaultListModel();
+               nodeList = new JList(nodeListModel);
+               nodeList.setName("node-list");
+               nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               nodeList.addListSelectionListener(this);
+               nodeList.setPreferredSize(new Dimension(250, -1));
+
+               nodeNameTextField = new JTextField("");
+               nodeNameTextField.getDocument().putProperty("Name", "node-name");
+               nodeNameTextField.getDocument().addDocumentListener(this);
+               nodeNameTextField.setEnabled(false);
+               
+               nodeHostnameTextField = new JTextField("localhost");
+               nodeHostnameTextField.getDocument().putProperty("Name", "node-hostname");
+               nodeHostnameTextField.getDocument().addDocumentListener(this);
+               nodeHostnameTextField.setEnabled(false);
+
+               nodePortSpinner = new JSpinner(new SpinnerNumberModel(9481, 1, 65535, 1));
+               nodePortSpinner.setName("node-port");
+               nodePortSpinner.addChangeListener(this);
+               nodePortSpinner.setEnabled(false);
+
+               JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
+               buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12));
+               buttonPanel.add(new JButton(addNodeAction));
+               buttonPanel.add(new JButton(deleteNodeAction));
+
+               JPanel centerPanel = new JPanel(new BorderLayout());
+               JPanel nodeInformationPanel = new JPanel(new GridBagLayout());
+               centerPanel.add(nodeInformationPanel, BorderLayout.PAGE_START);
+               nodeInformationPanel.add(buttonPanel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+               nodeInformationPanel.add(new JLabel("<html><b>" + I18n.getMessage("jsite.node-manager.node-information") + "</b></html>"), new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0));
+               nodeInformationPanel.add(new TLabel(I18n.getMessage("jsite.node-manager.name"), KeyEvent.VK_N, nodeNameTextField), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               nodeInformationPanel.add(nodeNameTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               nodeInformationPanel.add(new TLabel(I18n.getMessage("jsite.node-manager.hostname"), KeyEvent.VK_H, nodeHostnameTextField), new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               nodeInformationPanel.add(nodeHostnameTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               nodeInformationPanel.add(new TLabel(I18n.getMessage("jsite.node-manager.port"), KeyEvent.VK_P, nodePortSpinner), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               nodeInformationPanel.add(nodePortSpinner, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));
+
+               setLayout(new BorderLayout(12, 12));
+               add(new JScrollPane(nodeList), BorderLayout.LINE_START);
+               add(centerPanel, BorderLayout.CENTER);
+       }
+       
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               this.wizard = wizard;
+               wizard.setNextEnabled(nodeListModel.getSize() > 0);
+       }
+
+       public void setNodes(Node[] nodes) {
+               nodeListModel.clear();
+               for (Node node: nodes) {
+                       nodeListModel.addElement(node);
+               }
+               nodeList.repaint();
+               fireNodesUpdated(nodes);
+       }
+
+       public Node[] getNodes() {
+               Node[] returnNodes = new Node[nodeListModel.getSize()];
+               for (int nodeIndex = 0, nodeCount = nodeListModel.getSize(); nodeIndex < nodeCount; nodeIndex++) {
+                       returnNodes[nodeIndex] = (Node) nodeListModel.get(nodeIndex);
+               }
+               return returnNodes;
+       }
+
+       private Node getSelectedNode() {
+               return (Node) nodeList.getSelectedValue();
+       }
+       
+       private void updateTextField(DocumentEvent documentEvent) {
+               Node node = getSelectedNode();
+               if (node == null) {
+                       return;
+               }
+               Document document = documentEvent.getDocument();
+               String documentText = null;
+               try {
+                       documentText = document.getText(0, document.getLength());
+               } catch (BadLocationException ble1) {
+               }
+               if (documentText == null) {
+                       return;
+               }
+               String documentName = (String) document.getProperty("Name");
+               if ("node-name".equals(documentName)) {
+                       node.setName(documentText);
+                       nodeList.repaint();
+                       fireNodesUpdated(getNodes());
+               } else if ("node-hostname".equals(documentName)) {
+                       node.setHostname(documentText);
+                       nodeList.repaint();
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       protected void addNode() {
+               Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node"));
+               nodeListModel.addElement(node);
+               wizard.setNextEnabled(true);
+               fireNodesUpdated(getNodes());
+       }
+
+       protected void deleteNode() {
+               Node node = getSelectedNode();
+               if (node == null) {
+                       return;
+               }
+               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.node-manager.delete-node.warning"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.CANCEL_OPTION) {
+                       return;
+               }
+               nodeListModel.removeElement(node);
+               nodeList.repaint();
+               fireNodesUpdated(getNodes());
+               wizard.setNextEnabled(nodeListModel.size() > 0);
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void valueChanged(ListSelectionEvent e) {
+               Object source = e.getSource();
+               if (source instanceof JList) {
+                       JList sourceList = (JList) source;
+                       if ("node-list".equals(sourceList.getName())) {
+                               Node node = (Node) sourceList.getSelectedValue();
+                               boolean enabled = (node != null);
+                               nodeNameTextField.setEnabled(enabled);
+                               nodeHostnameTextField.setEnabled(enabled);
+                               nodePortSpinner.setEnabled(enabled);
+                               deleteNodeAction.setEnabled(enabled);
+                               if (enabled) {
+                                       nodeNameTextField.setText(node.getName());
+                                       nodeHostnameTextField.setText(node.getHostname());
+                                       nodePortSpinner.setValue(node.getPort());
+                               } else {
+                                       nodeNameTextField.setText("");
+                                       nodeHostnameTextField.setText("localhost");
+                                       nodePortSpinner.setValue(9481);
+                               }
+                       }
+               }
+       }
+
+       //
+       // INTERFACE DocumentListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void insertUpdate(DocumentEvent e) {
+               updateTextField(e);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void removeUpdate(DocumentEvent e) {
+               updateTextField(e);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void changedUpdate(DocumentEvent e) {
+               updateTextField(e);
+       }
+
+       //
+       // INTERFACE ChangeListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void stateChanged(ChangeEvent e) {
+               Object source = e.getSource();
+               Node selectedNode = getSelectedNode();
+               if (selectedNode == null) {
+                       return;
+               }
+               if (source instanceof JSpinner) {
+                       JSpinner sourceSpinner = (JSpinner) source;
+                       if ("node-port".equals(sourceSpinner.getName())) {
+                               selectedNode.setPort((Integer) sourceSpinner.getValue());
+                               nodeList.repaint();
+                       }
+               }
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/de/todesbaum/jsite/gui/ProjectFilesPage.java
new file mode 100644 (file)
index 0000000..1b5b8c6
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SwingUtilities;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+import de.todesbaum.jsite.application.EditionProject;
+import de.todesbaum.jsite.application.FileOption;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.mime.DefaultMIMETypes;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ProjectFilesPage.java 404 2006-03-26 02:11:03Z bombe $
+ */
+public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener, ChangeListener {
+
+       protected TWizard wizard;
+
+       protected Project project;
+
+       private Action scanAction;
+       private Action editContainerAction;
+       private Action addContainerAction;
+       private Action deleteContainerAction;
+
+       protected JList projectFileList;
+       private JCheckBox defaultFileCheckBox;
+       private JCheckBox fileOptionsInsertCheckBox;
+       private JTextField fileOptionsCustomKeyTextField;
+       private JComboBox fileOptionsMIMETypeComboBox;
+       protected DefaultComboBoxModel containerComboBoxModel;
+       private JComboBox fileOptionsContainerComboBox;
+       private JSpinner replaceEditionRangeSpinner;
+       private JCheckBox replacementCheckBox;
+
+       public ProjectFilesPage() {
+               super();
+               pageInit();
+       }
+
+       private void pageInit() {
+               createActions();
+               setLayout(new BorderLayout(12, 12));
+               add(createProjectFilesPanel(), BorderLayout.CENTER);
+       }
+
+       private void createActions() {
+               scanAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.rescan")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionScan();
+                       }
+               };
+               scanAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
+               scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip"));
+
+               addContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.add-container")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionAddContainer();
+                       }
+               };
+               addContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.add-container.tooltip"));
+               addContainerAction.setEnabled(false);
+
+               editContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.edit-container")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionEditContainer();
+                       }
+               };
+               editContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.edit-container.tooltip"));
+               editContainerAction.setEnabled(false);
+
+               deleteContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.delete-container")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionDeleteContainer();
+                       }
+               };
+               deleteContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.delete-container.tooltip"));
+               deleteContainerAction.setEnabled(false);
+       }
+
+       public void pageAdded(TWizard wizard) {
+               this.wizard = wizard;
+               actionScan();
+       }
+
+       private JComponent createProjectFilesPanel() {
+               JPanel projectFilesPanel = new JPanel(new BorderLayout(12, 12));
+
+               projectFileList = new JList();
+               projectFileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               projectFileList.setMinimumSize(new Dimension(250, projectFileList.getPreferredSize().height));
+               projectFileList.addListSelectionListener(this);
+
+               projectFilesPanel.add(new JScrollPane(projectFileList), BorderLayout.CENTER);
+
+               JPanel fileOptionsAlignmentPanel = new JPanel(new BorderLayout(12, 12));
+               projectFilesPanel.add(fileOptionsAlignmentPanel, BorderLayout.PAGE_END);
+               JPanel fileOptionsPanel = new JPanel(new GridBagLayout());
+               fileOptionsAlignmentPanel.add(fileOptionsPanel, BorderLayout.PAGE_START);
+
+               fileOptionsPanel.add(new JButton(scanAction), new GridBagConstraints(0, 0, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+
+               fileOptionsPanel.add(new JLabel("<html><b>" + I18n.getMessage("jsite.project-files.file-options") + "</b></html>"), new GridBagConstraints(0, 1, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0));
+
+               defaultFileCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.default"));
+               defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip"));
+               defaultFileCheckBox.setName("default-file");
+               defaultFileCheckBox.addActionListener(this);
+               defaultFileCheckBox.setEnabled(false);
+
+               fileOptionsPanel.add(defaultFileCheckBox, new GridBagConstraints(0, 2, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0));
+
+               fileOptionsInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert"), true);
+               fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip"));
+               fileOptionsInsertCheckBox.setName("insert");
+               fileOptionsInsertCheckBox.setMnemonic(KeyEvent.VK_I);
+               fileOptionsInsertCheckBox.addActionListener(this);
+               fileOptionsInsertCheckBox.setEnabled(false);
+
+               fileOptionsPanel.add(fileOptionsInsertCheckBox, new GridBagConstraints(0, 3, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               fileOptionsCustomKeyTextField = new JTextField(45);
+               fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip"));
+               fileOptionsCustomKeyTextField.setEnabled(false);
+               fileOptionsCustomKeyTextField.getDocument().addDocumentListener(this);
+
+               fileOptionsPanel.add(new TLabel(I18n.getMessage("jsite.project-files.custom-key"), KeyEvent.VK_K, fileOptionsCustomKeyTextField), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(1, 4, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               fileOptionsMIMETypeComboBox = new JComboBox(DefaultMIMETypes.getAllMIMETypes());
+               fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip"));
+               fileOptionsMIMETypeComboBox.setName("project-files.mime-type");
+               fileOptionsMIMETypeComboBox.addActionListener(this);
+               fileOptionsMIMETypeComboBox.setEnabled(false);
+
+               fileOptionsPanel.add(new TLabel(I18n.getMessage("jsite.project-files.mime-type"), KeyEvent.VK_M, fileOptionsMIMETypeComboBox), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               fileOptionsPanel.add(fileOptionsMIMETypeComboBox, new GridBagConstraints(1, 5, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               containerComboBoxModel = new DefaultComboBoxModel();
+               fileOptionsContainerComboBox = new JComboBox(containerComboBoxModel);
+               fileOptionsContainerComboBox.setToolTipText(I18n.getMessage("jsite.project-files.container.tooltip"));
+               fileOptionsContainerComboBox.setName("project-files.container");
+               fileOptionsContainerComboBox.addActionListener(this);
+               fileOptionsContainerComboBox.setEnabled(false);
+
+               fileOptionsPanel.add(new TLabel(I18n.getMessage("jsite.project-files.container"), KeyEvent.VK_C, fileOptionsContainerComboBox), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               fileOptionsPanel.add(fileOptionsContainerComboBox, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               fileOptionsPanel.add(new JButton(addContainerAction), new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               fileOptionsPanel.add(new JButton(editContainerAction), new GridBagConstraints(3, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               fileOptionsPanel.add(new JButton(deleteContainerAction), new GridBagConstraints(4, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               JPanel fileOptionsReplacementPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 6, 6));
+               fileOptionsReplacementPanel.setBorder(new EmptyBorder(-6, -6, -6, -6));
+
+               replacementCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.replacement"));
+               replacementCheckBox.setName("project-files.replace-edition");
+               replacementCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.replacement.tooltip"));
+               replacementCheckBox.addActionListener(this);
+               replacementCheckBox.setEnabled(false);
+               fileOptionsReplacementPanel.add(replacementCheckBox);
+
+               replaceEditionRangeSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1));
+               replaceEditionRangeSpinner.setName("project-files.replace-edition-range");
+               replaceEditionRangeSpinner.setToolTipText(I18n.getMessage("jsite.project-files.replacement.edition-range.tooltip"));
+               replaceEditionRangeSpinner.addChangeListener(this);
+               replaceEditionRangeSpinner.setEnabled(false);
+               fileOptionsReplacementPanel.add(new JLabel(I18n.getMessage("jsite.project-files.replacement.edition-range")));
+               fileOptionsReplacementPanel.add(replaceEditionRangeSpinner);
+
+               fileOptionsPanel.add(fileOptionsReplacementPanel, new GridBagConstraints(0, 7, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0));
+
+               return projectFilesPanel;
+       }
+
+       public void setProject(Project project) {
+               this.project = project;
+               setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName()));
+               setDescription(I18n.getMessage("jsite.project-files.description"));
+       }
+
+       private List<String> getProjectFiles() {
+               List<String> files = new ArrayList<String>();
+               for (int index = 0, size = projectFileList.getModel().getSize(); index < size; index++) {
+                       files.add((String) projectFileList.getModel().getElementAt(index));
+               }
+               return files;
+       }
+
+       protected void rebuildContainerComboBox() {
+               /* scan files for containers */
+               List<String> files = getProjectFiles();
+               List<String> containers = new ArrayList<String>(); // ComboBoxModel
+               // sucks. No
+               // contains()!
+               containers.add("");
+               for (String filename: files) {
+                       String container = project.getFileOption(filename).getContainer();
+                       if (!containers.contains(container)) {
+                               containers.add(container);
+                       }
+               }
+               Collections.sort(containers);
+               containerComboBoxModel.removeAllElements();
+               for (String container: containers) {
+                       containerComboBoxModel.addElement(container);
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       protected void actionScan() {
+               projectFileList.clearSelection();
+               projectFileList.setListData(new Object[0]);
+
+               wizard.setNextEnabled(false);
+               wizard.setPreviousEnabled(false);
+               wizard.setQuitEnabled(false);
+
+               FileScanner fileScanner = new FileScanner(project);
+               fileScanner.addFileScannerListener(this);
+               new Thread(fileScanner).start();
+       }
+
+       protected void actionAddContainer() {
+               String containerName = JOptionPane.showInputDialog(wizard, I18n.getMessage("jsite.project-files.action.add-container.message") + ":", null, JOptionPane.INFORMATION_MESSAGE);
+               if (containerName == null) {
+                       return;
+               }
+               containerName = containerName.trim();
+               String filename = (String) projectFileList.getSelectedValue();
+               FileOption fileOption = project.getFileOption(filename);
+               fileOption.setContainer(containerName);
+               rebuildContainerComboBox();
+               fileOptionsContainerComboBox.setSelectedItem(containerName);
+       }
+
+       protected void actionEditContainer() {
+               String selectedFilename = (String) projectFileList.getSelectedValue();
+               FileOption fileOption = project.getFileOption(selectedFilename);
+               String oldContainerName = fileOption.getContainer();
+               String containerName = JOptionPane.showInputDialog(wizard, I18n.getMessage("jsite.project-files.action.edit-container.message") + ":", oldContainerName);
+               if (containerName == null) {
+                       return;
+               }
+               if (containerName.equals("")) {
+                       fileOption.setContainer("");
+                       fileOptionsContainerComboBox.setSelectedItem("");
+                       return;
+               }
+               List<String> files = getProjectFiles();
+               for (String filename: files) {
+                       fileOption = project.getFileOption(filename);
+                       if (fileOption.getContainer().equals(oldContainerName)) {
+                               fileOption.setContainer(containerName);
+                       }
+               }
+               rebuildContainerComboBox();
+               fileOptionsContainerComboBox.setSelectedItem(containerName);
+       }
+
+       protected void actionDeleteContainer() {
+               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.project-files.action.delete-container.message"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) {
+                       String containerName = (String) fileOptionsContainerComboBox.getSelectedItem();
+                       List<String> files = getProjectFiles();
+                       for (String filename: files) {
+                               FileOption fileOption = project.getFileOption(filename);
+                               if (fileOption.getContainer().equals(containerName)) {
+                                       fileOption.setContainer("");
+                               }
+                       }
+                       fileOptionsContainerComboBox.setSelectedItem("");
+               }
+       }
+
+       public void fileScannerFinished(FileScanner fileScanner) {
+               final boolean error = fileScanner.isError();
+               if (!error) {
+                       final List<String> files = fileScanner.getFiles();
+                       SwingUtilities.invokeLater(new Runnable() {
+
+                               public void run() {
+                                       projectFileList.setListData(files.toArray(new String[files.size()]));
+                                       projectFileList.clearSelection();
+                                       rebuildContainerComboBox();
+                               }
+                       });
+               } else {
+                       JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.scan-error"), null, JOptionPane.ERROR_MESSAGE);
+               }
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       public void run() {
+                               wizard.setPreviousEnabled(true);
+                               wizard.setNextEnabled(!error);
+                               wizard.setQuitEnabled(true);
+                       }
+               });
+       }
+
+       //
+       // INTERFACE ActionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void actionPerformed(ActionEvent actionEvent) {
+               String filename = (String) projectFileList.getSelectedValue();
+               if (filename == null) {
+                       return;
+               }
+               FileOption fileOption = project.getFileOption(filename);
+               Object source = actionEvent.getSource();
+               if (source instanceof JCheckBox) {
+                       JCheckBox checkBox = (JCheckBox) source;
+                       if ("default-file".equals(checkBox.getName())) {
+                               if (checkBox.isSelected()) {
+                                       project.setIndexFile(filename);
+                               } else {
+                                       project.setIndexFile(null);
+                               }
+                       } else if ("insert".equals(checkBox.getName())) {
+                               boolean isInsert = checkBox.isSelected();
+                               fileOptionsCustomKeyTextField.setEnabled(!isInsert);
+                               fileOption.setInsert(isInsert);
+                               if (!isInsert) {
+                                       fileOptionsContainerComboBox.setSelectedItem("");
+                               }
+                       } else if ("project-files.replace-edition".equals(checkBox.getName())) {
+                               boolean replaceEdition = checkBox.isSelected();
+                               fileOption.setReplaceEdition(replaceEdition);
+                               replaceEditionRangeSpinner.setEnabled(replaceEdition);
+                       }
+               } else if (source instanceof JComboBox) {
+                       JComboBox comboBox = (JComboBox) source;
+                       if ("project-files.mime-type".equals(comboBox.getName())) {
+                               fileOption.setMimeType((String) comboBox.getSelectedItem());
+                       } else if ("project-files.container".equals(comboBox.getName())) {
+                               String containerName = (String) comboBox.getSelectedItem();
+                               fileOption.setContainer(containerName);
+                               boolean enabled = !"".equals(containerName);
+                               editContainerAction.setEnabled(enabled);
+                               deleteContainerAction.setEnabled(enabled);
+                               if (enabled) {
+                                       fileOptionsInsertCheckBox.setSelected(true);
+                               }
+                       }
+               }
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void valueChanged(ListSelectionEvent e) {
+               String filename = (String) projectFileList.getSelectedValue();
+               boolean enabled = filename != null;
+               boolean insert = fileOptionsInsertCheckBox.isSelected();
+               defaultFileCheckBox.setEnabled(enabled);
+               fileOptionsInsertCheckBox.setEnabled(enabled);
+               fileOptionsCustomKeyTextField.setEnabled(enabled && !insert);
+               fileOptionsMIMETypeComboBox.setEnabled(enabled);
+               fileOptionsContainerComboBox.setEnabled(enabled);
+               addContainerAction.setEnabled(enabled);
+               editContainerAction.setEnabled(enabled);
+               deleteContainerAction.setEnabled(enabled);
+               replacementCheckBox.setEnabled(enabled && insert && (project instanceof EditionProject));
+               if (filename != null) {
+                       FileOption fileOption = project.getFileOption(filename);
+                       defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile()));
+                       fileOptionsInsertCheckBox.setSelected(fileOption.isInsert());
+                       fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey());
+                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType());
+                       fileOptionsContainerComboBox.setSelectedItem(fileOption.getContainer());
+                       replacementCheckBox.setSelected(fileOption.getReplaceEdition());
+                       replaceEditionRangeSpinner.setValue(fileOption.getEditionRange());
+                       replaceEditionRangeSpinner.setEnabled(fileOption.getReplaceEdition());
+               } else {
+                       defaultFileCheckBox.setSelected(false);
+                       fileOptionsInsertCheckBox.setSelected(true);
+                       fileOptionsCustomKeyTextField.setText("CHK@");
+                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE);
+                       fileOptionsContainerComboBox.setSelectedItem("");
+                       replacementCheckBox.setSelected(false);
+                       replaceEditionRangeSpinner.setValue(0);
+               }
+       }
+
+       //
+       // INTERFACE DocumentListener
+       //
+
+       private void processDocumentUpdate(DocumentEvent documentEvent) {
+               String filename = (String) projectFileList.getSelectedValue();
+               if (filename == null)
+                       return;
+               FileOption fileOption = project.getFileOption(filename);
+               Document document = documentEvent.getDocument();
+               try {
+                       String text = document.getText(0, document.getLength());
+                       fileOption.setCustomKey(text);
+               } catch (BadLocationException ble1) {
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void changedUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void insertUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void removeUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+       //
+       // INTERFACE ChangeListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void stateChanged(ChangeEvent changeEvent) {
+               String filename = (String) projectFileList.getSelectedValue();
+               if (filename == null)
+                       return;
+               FileOption fileOption = project.getFileOption(filename);
+               Object source = changeEvent.getSource();
+               if (source instanceof JSpinner) {
+                       JSpinner spinner = (JSpinner) source;
+                       fileOption.setEditionRange((Integer) spinner.getValue());
+               }
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/de/todesbaum/jsite/gui/ProjectInsertPage.java
new file mode 100644 (file)
index 0000000..ef06fc5
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Date;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+
+import de.todesbaum.jsite.application.EditionProject;
+import de.todesbaum.jsite.application.Freenet7Interface;
+import de.todesbaum.jsite.application.InsertListener;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.application.ProjectInserter;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ProjectInsertPage.java 408 2006-03-29 09:31:10Z bombe $
+ */
+public class ProjectInsertPage extends TWizardPage implements InsertListener {
+
+       protected TWizard wizard;
+       protected ProjectInserter projectInserter;
+
+       protected JTextField requestURITextField;
+       protected JLabel startTimeLabel;
+       protected JProgressBar progressBar;
+       protected long startTime;
+
+       public ProjectInsertPage() {
+               super();
+               pageInit();
+               setHeading(I18n.getMessage("jsite.insert.heading"));
+               setDescription(I18n.getMessage("jsite.insert.description"));
+               projectInserter = new ProjectInserter();
+               projectInserter.addInsertListener(this);
+       }
+
+       private void pageInit() {
+               setLayout(new BorderLayout(12, 12));
+               add(createProjectInsertPanel(), BorderLayout.CENTER);
+       }
+
+       private JComponent createProjectInsertPanel() {
+               JComponent projectInsertPanel = new JPanel(new GridBagLayout());
+
+               requestURITextField = new JTextField();
+               requestURITextField.setEditable(false);
+               requestURITextField.setBackground(getBackground());
+               requestURITextField.setBorder(null);
+
+               startTimeLabel = new JLabel();
+
+               progressBar = new JProgressBar(0, 1);
+               progressBar.setStringPainted(true);
+               progressBar.setValue(0);
+
+               projectInsertPanel.add(new JLabel("<html><b>" + I18n.getMessage("jsite.insert.project-information") + "</b></html>"), new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+               projectInsertPanel.add(new JLabel(I18n.getMessage("jsite.insert.request-uri") + ":"), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0));
+               projectInsertPanel.add(requestURITextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               projectInsertPanel.add(new JLabel(I18n.getMessage("jsite.insert.start-time") + ":"), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0));
+               projectInsertPanel.add(startTimeLabel, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               projectInsertPanel.add(new JLabel(I18n.getMessage("jsite.insert.progress") + ":"), new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0));
+               projectInsertPanel.add(progressBar, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               return projectInsertPanel;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               this.wizard = wizard;
+               wizard.setPreviousEnabled(false);
+               wizard.setNextEnabled(false);
+               wizard.setQuitEnabled(false);
+               progressBar.setValue(0);
+               projectInserter.start();
+       }
+
+       /**
+        * @param debug
+        *            The debug to set.
+        */
+       public void setDebug(boolean debug) {
+               projectInserter.setDebug(debug);
+       }
+
+       /**
+        * @param project
+        *            The project to set.
+        */
+       public void setProject(final Project project) {
+               projectInserter.setProject(project);
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       public void run() {
+                               StringBuffer uriBuffer = new StringBuffer();
+                               uriBuffer.append(project.getRequestURI());
+                               uriBuffer.append(project.getPath());
+                               if (project instanceof EditionProject) {
+                                       uriBuffer.append('-').append(((EditionProject) project).getEdition());
+                               }
+                               uriBuffer.append('/');
+                               requestURITextField.setText(uriBuffer.toString());
+                       }
+               });
+       }
+
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               projectInserter.setFreenetInterface(freenetInterface);
+       }
+
+       //
+       // INTERFACE InsertListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertStarted(final Project project) {
+               startTime = System.currentTimeMillis();
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       public void run() {
+                               startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date(startTime)));
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertProgress(Project project, final int succeeded, final int failed, final int fatal, final int total, final boolean finalized) {
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       public void run() {
+                               progressBar.setMaximum(total);
+                               progressBar.setValue(succeeded + failed + fatal);
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertFinished(Project project, boolean success, Throwable cause) {
+               if (success) {
+                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.inserted"), null, JOptionPane.INFORMATION_MESSAGE);
+               } else {
+                       if (cause == null) {
+                               JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-failed"), null, JOptionPane.ERROR_MESSAGE);
+                       } else {
+                               JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.insert.insert-failed-with-cause"), cause.getMessage()), null, JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       public void run() {
+                               wizard.setNextEnabled(true);
+                               wizard.setQuitEnabled(true);
+                       }
+               });
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/gui/ProjectPage.java b/src/de/todesbaum/jsite/gui/ProjectPage.java
new file mode 100644 (file)
index 0000000..96a027d
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.JSpinner.NumberEditor;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+import de.todesbaum.jsite.application.EditionProject;
+import de.todesbaum.jsite.application.Freenet7Interface;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.swing.SortedListModel;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ProjectPage.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public class ProjectPage extends TWizardPage implements ListSelectionListener, ChangeListener, DocumentListener {
+
+       private Freenet7Interface freenetInterface;
+
+       private Action projectLocalPathBrowseAction;
+       private Action projectAddAction;
+       private Action projectDeleteAction;
+       private Action projectCloneAction;
+
+       private JFileChooser pathChooser;
+       private SortedListModel projectListModel;
+       private JList projectList;
+       private JTextField projectNameTextField;
+       private JTextField projectDescriptionTextField;
+       private JTextField projectLocalPathTextField;
+       private JTextField projectPublicKeyTextField;
+       private JTextField projectPrivateKeyTextField;
+       private JTextField projectPathTextField;
+       private JSpinner projectEditionSpinner;
+
+       public ProjectPage() {
+               super();
+               setLayout(new BorderLayout(12, 12));
+               dialogInit();
+               setHeading(I18n.getMessage("jsite.project.heading"));
+               setDescription(I18n.getMessage("jsite.project.description"));
+       }
+
+       protected void dialogInit() {
+               createActions();
+
+               pathChooser = new JFileChooser();
+               projectListModel = new SortedListModel();
+               projectList = new JList(projectListModel);
+               projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               projectList.addListSelectionListener(this);
+               projectList.setPreferredSize(new Dimension(150, projectList.getPreferredSize().height));
+
+               add(new JScrollPane(projectList), BorderLayout.LINE_START);
+               add(createInformationPanel(), BorderLayout.CENTER);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               super.pageAdded(wizard);
+               projectList.clearSelection();
+               wizard.setNextEnabled(false);
+       }
+
+       /**
+        */
+       public void addListSelectionListener(ListSelectionListener listener) {
+               projectList.addListSelectionListener(listener);
+       }
+
+       /**
+        */
+       public void removeListSelectionListener(ListSelectionListener listener) {
+               projectList.removeListSelectionListener(listener);
+       }
+
+       private void createActions() {
+               projectLocalPathBrowseAction = new AbstractAction(I18n.getMessage("jsite.project.action.browse")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionLocalPathBrowse();
+                       }
+               };
+               projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip"));
+               projectLocalPathBrowseAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_B);
+               projectLocalPathBrowseAction.setEnabled(false);
+
+               projectAddAction = new AbstractAction(I18n.getMessage("jsite.project.action.add-project")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionAdd();
+                       }
+               };
+               projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip"));
+               projectAddAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_A);
+
+               projectDeleteAction = new AbstractAction(I18n.getMessage("jsite.project.action.delete-project")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionDelete();
+                       }
+               };
+               projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip"));
+               projectDeleteAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D);
+               projectDeleteAction.setEnabled(false);
+
+               projectCloneAction = new AbstractAction(I18n.getMessage("jsite.project.action.clone-project")) {
+
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionClone();
+                       }
+               };
+               projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip"));
+               projectCloneAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_L);
+               projectCloneAction.setEnabled(false);
+       }
+
+       private JComponent createInformationPanel() {
+               JPanel informationPanel = new JPanel(new BorderLayout(12, 12));
+
+               JPanel informationTable = new JPanel(new GridBagLayout());
+
+               JPanel functionButtons = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
+               functionButtons.setBorder(new EmptyBorder(-12, -12, -12, -12));
+               functionButtons.add(new JButton(projectAddAction));
+               functionButtons.add(new JButton(projectDeleteAction));
+               functionButtons.add(new JButton(projectCloneAction));
+
+               informationPanel.add(functionButtons, BorderLayout.PAGE_START);
+               informationPanel.add(informationTable, BorderLayout.CENTER);
+
+               informationTable.add(new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.information") + "</b></html>"), new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+
+               projectNameTextField = new JTextField();
+               projectNameTextField.getDocument().putProperty("name", "project.name");
+               projectNameTextField.getDocument().addDocumentListener(this);
+               projectNameTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.name") + ":", KeyEvent.VK_N, projectNameTextField), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectNameTextField, new GridBagConstraints(1, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectDescriptionTextField = new JTextField();
+               projectDescriptionTextField.getDocument().putProperty("name", "project.description");
+               projectDescriptionTextField.getDocument().addDocumentListener(this);
+               projectDescriptionTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.description") + ":", KeyEvent.VK_D, projectDescriptionTextField), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectDescriptionTextField, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectLocalPathTextField = new JTextField();
+               projectLocalPathTextField.getDocument().putProperty("name", "project.localpath");
+               projectLocalPathTextField.getDocument().addDocumentListener(this);
+               projectLocalPathTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.local-path") + ":", KeyEvent.VK_L, projectLocalPathTextField), new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectLocalPathTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               informationTable.add(new JButton(projectLocalPathBrowseAction), new GridBagConstraints(2, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               informationTable.add(new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.address") + "</b></html>"), new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
+
+               projectPublicKeyTextField = new JTextField(27);
+               projectPublicKeyTextField.getDocument().putProperty("name", "project.publickey");
+               projectPublicKeyTextField.getDocument().addDocumentListener(this);
+               projectPublicKeyTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.public-key") + ":", KeyEvent.VK_U, projectPublicKeyTextField), new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectPublicKeyTextField, new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectPrivateKeyTextField = new JTextField(27);
+               projectPrivateKeyTextField.getDocument().putProperty("name", "project.privatekey");
+               projectPrivateKeyTextField.getDocument().addDocumentListener(this);
+               projectPrivateKeyTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.private-key") + ":", KeyEvent.VK_R, projectPrivateKeyTextField), new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectPrivateKeyTextField, new GridBagConstraints(1, 6, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectPathTextField = new JTextField();
+               projectPathTextField.getDocument().putProperty("name", "project.path");
+               projectPathTextField.getDocument().addDocumentListener(this);
+               projectPathTextField.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.path") + ":", KeyEvent.VK_P, projectPathTextField), new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectPathTextField, new GridBagConstraints(1, 7, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectEditionSpinner = new JSpinner(new SpinnerNumberModel(1, 0, Integer.MAX_VALUE, 1));
+               ((NumberEditor) projectEditionSpinner.getEditor()).getTextField().setColumns(6);
+               projectEditionSpinner.setName("project.edition");
+               projectEditionSpinner.addChangeListener(this);
+               projectEditionSpinner.setEnabled(false);
+
+               informationTable.add(new TLabel(I18n.getMessage("jsite.project.project.edition") + ":", KeyEvent.VK_E, projectEditionSpinner), new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               informationTable.add(projectEditionSpinner, new GridBagConstraints(1, 8, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));
+
+               return informationPanel;
+       }
+
+       public void setProjects(Project[] projects) {
+               projectListModel.clear();
+               for (Project project: projects) {
+                       projectListModel.add(project);
+               }
+       }
+
+       public Project[] getProjects() {
+               return (Project[]) projectListModel.toArray(new Project[projectListModel.size()]);
+       }
+
+       /**
+        * @param freenetInterface
+        *            The freenetInterface to set.
+        */
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       public Project getSelectedProject() {
+               return (Project) projectList.getSelectedValue();
+       }
+
+       private void setTextField(DocumentEvent documentEvent) {
+               Document document = documentEvent.getDocument();
+               String propertyName = (String) document.getProperty("name");
+               Project project = (Project) projectList.getSelectedValue();
+               if (project == null) {
+                       return;
+               }
+               try {
+                       String text = document.getText(0, document.getLength()).trim();
+                       if ("project.name".equals(propertyName)) {
+                               project.setName(text);
+                               projectList.repaint();
+                       } else if ("project.description".equals(propertyName)) {
+                               project.setDescription(text);
+                       } else if ("project.localpath".equals(propertyName)) {
+                               project.setLocalPath(text);
+                       } else if ("project.privatekey".equals(propertyName)) {
+                               project.setInsertURI(text);
+                       } else if ("project.publickey".equals(propertyName)) {
+                               project.setRequestURI(text);
+                       } else if ("project.path".equals(propertyName)) {
+                               project.setPath(text);
+                       }
+               } catch (BadLocationException e) {
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       protected void actionLocalPathBrowse() {
+               Project project = (Project) projectList.getSelectedValue();
+               if (project == null)
+                       return;
+               pathChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+               if (pathChooser.showDialog(this, I18n.getMessage("jsite.project.action.browse.choose")) == JFileChooser.APPROVE_OPTION) {
+                       projectLocalPathTextField.setText(pathChooser.getSelectedFile().getPath());
+               }
+       }
+
+       protected void actionAdd() {
+               String[] keyPair = null;
+               if (!freenetInterface.hasNode()) {
+                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.project-files.no-node-selected"), null, JOptionPane.ERROR_MESSAGE);
+                       return;
+               }
+               try {
+                       keyPair = freenetInterface.generateKeyPair();
+               } catch (IOException ioe1) {
+                       JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.keygen.io-error"), ioe1.getMessage()), null, JOptionPane.ERROR_MESSAGE);
+                       return;
+               }
+               EditionProject newProject = new EditionProject();
+               newProject.setName(I18n.getMessage("jsite.project.new-project.name"));
+               newProject.setInsertURI(keyPair[0]);
+               newProject.setRequestURI(keyPair[1]);
+               newProject.setEdition(1);
+               projectListModel.add(newProject);
+               projectList.setSelectedIndex(projectListModel.size() - 1);
+       }
+
+       protected void actionDelete() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       if (JOptionPane.showConfirmDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.action.delete-project.confirm"), ((Project) projectList.getSelectedValue()).getName()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) {
+                               projectListModel.remove(selectedIndex);
+                               projectList.clearSelection();
+                               if (projectListModel.getSize() != 0) {
+                                       projectList.setSelectedIndex(Math.min(selectedIndex, projectListModel.getSize() - 1));
+                               }
+                       }
+               }
+       }
+
+       protected void actionClone() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project newProject = null;
+                       Project selectedProject = (Project) projectList.getSelectedValue();
+                       if (selectedProject instanceof EditionProject) {
+                               newProject = new EditionProject(selectedProject);
+                       } // else { /* BUG! */ }
+                       newProject.setName(MessageFormat.format(I18n.getMessage("jsite.project.action.clone-project.copy"), newProject.getName()));
+                       projectListModel.add(newProject);
+                       projectList.setSelectedIndex(projectListModel.indexOf(newProject));
+               }
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void valueChanged(ListSelectionEvent listSelectionEvent) {
+               int selectedRow = projectList.getSelectedIndex();
+               Project selectedProject = (Project) projectList.getSelectedValue();
+               projectNameTextField.setEnabled(selectedRow > -1);
+               projectDescriptionTextField.setEnabled(selectedRow > -1);
+               projectLocalPathTextField.setEnabled(selectedRow > -1);
+               projectPublicKeyTextField.setEnabled(selectedRow > -1);
+               projectPrivateKeyTextField.setEnabled(selectedRow > -1);
+               projectPathTextField.setEnabled(selectedRow > -1);
+               projectEditionSpinner.setEnabled(selectedRow > -1);
+               projectLocalPathBrowseAction.setEnabled(selectedRow > -1);
+               projectDeleteAction.setEnabled(selectedRow > -1);
+               projectCloneAction.setEnabled(selectedRow > -1);
+               if (selectedRow > -1) {
+                       projectNameTextField.setText(selectedProject.getName());
+                       projectDescriptionTextField.setText(selectedProject.getDescription());
+                       projectLocalPathTextField.setText(selectedProject.getLocalPath());
+                       projectPublicKeyTextField.setText(selectedProject.getRequestURI());
+                       projectPrivateKeyTextField.setText(selectedProject.getInsertURI());
+                       projectPathTextField.setText(selectedProject.getPath());
+                       if (selectedProject instanceof EditionProject) {
+                               EditionProject editionProject = (EditionProject) selectedProject;
+                               projectEditionSpinner.setValue(editionProject.getEdition());
+                       }
+               } else {
+                       projectNameTextField.setText("");
+                       projectDescriptionTextField.setText("");
+                       projectLocalPathTextField.setText("");
+                       projectPublicKeyTextField.setText("");
+                       projectPrivateKeyTextField.setText("");
+                       projectPathTextField.setText("");
+                       projectEditionSpinner.setValue(0);
+               }
+       }
+
+       //
+       // INTERFACE ChangeListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void stateChanged(ChangeEvent changeEvent) {
+               Object source = changeEvent.getSource();
+               if (source instanceof JSpinner) {
+                       JSpinner spinner = (JSpinner) source;
+                       Project currentProject = (Project) projectList.getSelectedValue();
+                       if (currentProject == null) {
+                               return;
+                       }
+                       if ("project.edition".equals(spinner.getName())) {
+                               ((EditionProject) currentProject).setEdition((Integer) spinner.getValue());
+                       }
+               }
+       }
+
+       //
+       // INTERFACE DocumentListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void insertUpdate(DocumentEvent documentEvent) {
+               setTextField(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void removeUpdate(DocumentEvent documentEvent) {
+               setTextField(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void changedUpdate(DocumentEvent documentEvent) {
+               setTextField(documentEvent);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/i18n/I18n.java b/src/de/todesbaum/jsite/i18n/I18n.java
new file mode 100644 (file)
index 0000000..38a6916
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.i18n;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: I18n.java 355 2006-03-24 15:04:11Z bombe $
+ */
+public class I18n {
+
+       private static Locale currentLocale;
+
+       public static Locale getLocale() {
+               if (currentLocale == null)
+                       currentLocale = Locale.getDefault();
+               return currentLocale;
+       }
+
+       public static void setLocale(Locale locale) {
+               currentLocale = locale;
+               Locale.setDefault(locale);
+       }
+
+       public static ResourceBundle getResourceBundle() {
+               return getResourceBundle(getLocale());
+       }
+
+       public static ResourceBundle getResourceBundle(Locale locale) {
+               return ResourceBundle.getBundle("de.todesbaum.jsite.i18n.jSite", locale);
+       }
+
+       public static String getMessage(String key) {
+               return getResourceBundle().getString(key);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/i18n/jSite.properties b/src/de/todesbaum/jsite/i18n/jSite.properties
new file mode 100644 (file)
index 0000000..204de9a
--- /dev/null
@@ -0,0 +1,126 @@
+#
+# jSite - a tool for uploading websites into Freenet
+# Copyright (C) 2006 David Roden
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# English language file by David Roden <droden@gmail.com>
+
+jsite.main.already-running=<html><b>jSite is already running</b><br><br>A lock file has been found that suggests that another<br>instance of jSite is already running. Running multiple instances<br>of jSite is guaranteed to break your configuration.</htm>
+
+jsite.wizard.previous=Previous
+jsite.wizard.next=Next
+jsite.wizard.quit=Quit
+
+jsite.quit.question=Do you really want to quit?
+jsite.quit.config-not-saved=<html><b>Configuration not saved</b><br><br>The configuration could not be saved.<br>Do you want to quit anyway?</html>
+
+jsite.menu.languages=Languages
+jsite.menu.language.change.restart-message=<html><b>Restart necessary</b><br><br>For language changes to take effect,<br>jSite must be restarted!</html>
+jsite.menu.language.en=English
+jsite.menu.language.de=German
+jsite.menu.nodes=Nodes
+jsite.menu.nodes.manage-nodes=Manage nodes
+jsite.menu.help=Help
+jsite.menu.help.about=About
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006 David Roden<br>Released under the GNU General Public License</html>
+
+jsite.node-manager.heading=Node Manager
+jsite.node-manager.description=Manage your nodes here.
+jsite.node-manager.node-information=Node Information
+jsite.node-manager.add-node=Add Node
+jsite.node-manager.new-node=New Node
+jsite.node-manager.delete-node=Delete Node
+jsite.node-manager.delete-node.warning=<html><b>Confirm node deletion</b><br><br>Really delete this node?</html>
+jsite.node-manager.name=Name
+jsite.node-manager.hostname=Hostname
+jsite.node-manager.port=Port
+
+jsite.insert.heading=Project insert
+jsite.insert.description=Please wait while the project is being inserted.
+jsite.insert.project-information=Project information
+jsite.insert.request-uri=Freesite
+jsite.insert.start-time=Start time
+jsite.insert.progress=Progress
+jsite.insert.insert-failed=<html><b>Insert failed</b><br><br>The insert of the project failed.<br>Some files could not be inserted.</html>
+jsite.insert.insert-failed-with-cause=<html><b>Insert failed</b><br><br>The insert of the project failed.<br>Some files could not be inserted.<br>The following error occured:<br><br><code>{0}</code></html>
+jsite.insert.inserted=<html><b>Project inserted</b><br><br>Your project was inserted successfully.</html>
+
+jsite.file-scanner.can-not-read-directory=Can not read directory
+
+jsite.project.heading=Select a Project
+jsite.project.description=Select a project to process from the list below, or create a new project.
+jsite.project.action.browse=Browse
+jsite.project.action.browse.choose=Choose
+jsite.project.action.browse.tooltip=Browse for directory
+jsite.project.action.add-project=Add project
+jsite.project.action.add-project.tooltip=Add a new project
+jsite.project.new-project.name=New Project
+jsite.project.action.delete-project=Delete project
+jsite.project.action.delete-project.tooltip=Delete a project
+jsite.project.action.delete-project.confirm=<html><b>Confirm deletion</b><br><br>The project \u201c{0}\u201d will be deleted!<br>Do you want to continue?</html>
+jsite.project.action.clone-project=Clone project
+jsite.project.action.clone-project.copy=Copy of {0}
+jsite.project.action.clone-project.tooltip=Clone the selected project
+jsite.project.project.information=Project Information
+jsite.project.project.name=Name
+jsite.project.project.description=Description
+jsite.project.project.local-path=Local path
+jsite.project.project.address=Address
+jsite.project.project.public-key=Request URI
+jsite.project.project.private-key=Insert URI
+jsite.project.project.path=Path
+jsite.project.project.edition=Edition
+jsite.project.keygen.io-error=<html><b>Node communication failure</b><br><br>Communication with the node failed<br>with the following error message:<br><br><code>{0}</code><br><br>Please make sure that you have entered<br>the correct host name and port number<br>on the "Node Settings" page.</html>
+jsite.project.warning.no-local-path=<html><b>No local path</b><br><br>You did not specify a local path for the files to insert.<br>It is not possible to continue without one.</html>
+jsite.project.warning.no-path=<html><b>No freesite path</b><br><br>You did not specify a freesite path.<br>It is not possible to continue without one.</html>
+
+jsite.project-files.heading=Project Files
+jsite.project-files.description=<html>On this page you can specify parameters for the files within the project, such as<br>externally generated keys or MIME types, if the automatic detection failed.</html> 
+jsite.project-files.action.rescan=Re-scan
+jsite.project-files.action.rescan.tooltip=Re-scan the project directory for new files
+jsite.project-files.action.add-container=Add
+jsite.project-files.action.add-container.tooltip=Adds a new container to the project and this file to it
+jsite.project-files.action.add-container.message=Enter the name of the new container
+jsite.project-files.action.edit-container=Edit
+jsite.project-files.action.edit-container.tooltip=Changes the name of the container
+jsite.project-files.action.edit-container.message=Enter the new name of the container
+jsite.project-files.action.delete-container=Delete
+jsite.project-files.action.delete-container.tooltip=Deletes this container
+jsite.project-files.action.delete-container.message=Do you really want to delete this container?
+jsite.project-files.file-options=File Options
+jsite.project-files.default=Default file
+jsite.project-files.default.tooltip=Specify that this file is the project\u2019s index file
+jsite.project-files.insert=Insert
+jsite.project-files.insert.tooltip=Uncheck if this file was inserted externally
+jsite.project-files.custom-key=Custom key
+jsite.project-files.custom-key.tooltip=The externally created key for the file
+jsite.project-files.mime-type=MIME type
+jsite.project-files.mime-type.tooltip=Select the correct MIME type here if the detection failed
+jsite.project-files.container=Container
+jsite.project-files.container.tooltip=Selects a container for the current file
+jsite.project-files.replacement=Replacements
+jsite.project-files.replacement.tooltip=Activates replacements in file
+jsite.project-files.replacement.edition-range=Range
+jsite.project-files.replacement.edition-range.tooltip=Also replace $[EDITION+1], $[EDITION+2]\u2026
+jsite.project-files.scan-error=<html><b>Error scanning files</b><br><br>Either the directory of the project does not exist<br>or some files/directories in it are notaccessible.<br>Please go back and select the correct directory.</html>
+jsite.project-files.empty-index=<html><b>No default file</b><br><br>You did not specify a default file for this project.<br>While it is possible to insert a project without a default<br>file you should specify one to ease browsing.</html>
+jsite.project-files.container-index=<html><b>Default file in container</b><br><br>Your default file was placed in a container!<br>This might make other people shun your page.</html>
+jsite.project-files.index-not-html=<html><b>Default file is not HTML</b><br><br>Your default file does not have the MIME type "text/html"!<br>Loading your Freesite in a browser may give unexpected results.</html>
+jsite.project-files.no-node-running=<html><b>Node is not running</b><br><br>You can not insert a project if your node is not running.<br>Please start your node and try again.</html>
+jsite.project-files.no-custom-key=<html><b>No custom key for file</b><br><br>You specified not to insert <code>{0}</code><br>but failed to enter a key to redirect to!</html>
+jsite.project-files.no-node-selected=<html><b>No node selected</b><br><br>Please select a node from the menu!</html>
diff --git a/src/de/todesbaum/jsite/i18n/jSite_de.properties b/src/de/todesbaum/jsite/i18n/jSite_de.properties
new file mode 100644 (file)
index 0000000..f9ec5d1
--- /dev/null
@@ -0,0 +1,126 @@
+#
+# jSite - a tool for uploading websites into Freenet
+# Copyright (C) 2006 David Roden
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# German language file by David Roden <droden@gmail.com>
+
+jsite.main.already-running=<html><b>jSite läuft bereits!</b><br><br>Es wurde festgestellt, dass jSite bereits läuft. Das kann<br>zu Beschädigungen an der Konfiguration führen.</html>
+
+jsite.wizard.previous=Zurück
+jsite.wizard.next=Vorwärts
+jsite.wizard.quit=Beenden
+
+jsite.quit.question=Möchten Sie jSite wirklich beenden?
+jsite.quit.config-not-saved=<html><b>Konfiguration nicht gespeichert</b><br><br>Die Konfiguration konnte nicht gespeichert werden.<br>Soll jSite trotzdem beendet werden?</html>
+
+jsite.menu.languages=Sprachen
+jsite.menu.language.change.restart-message=<html><b>Neustart notwending</b><br><br>Damit die Änderungen an der Sprache wirksam<br>werden, muss jSite neu gestartet werden!</html>
+jsite.menu.language.en=Englisch
+jsite.menu.language.de=Deutsch
+jsite.menu.nodes=Nodes
+jsite.menu.nodes.manage-nodes=Nodes verwalten
+jsite.menu.help=Hilfe
+jsite.menu.help.about=Über
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006 David Roden<br>Veröffentlicht unter der GNU General Public License</html>
+
+jsite.node-manager.heading=Nodeverwaltung
+jsite.node-manager.description=Verwalten Sie hier Ihre Nodes.
+jsite.node-manager.node-information=Nodeinformation
+jsite.node-manager.add-node=Node hinzufügen
+jsite.node-manager.new-node=Neuer Node
+jsite.node-manager.delete-node=Node löschen
+jsite.node-manager.delete-node.warning=<html><b>Nodelöschung bestätigen</b><br><br>Wollen Sie diesen Node wirklich löschen?</html>
+jsite.node-manager.name=Name
+jsite.node-manager.hostname=Hostname
+jsite.node-manager.port=Port
+
+jsite.insert.heading=Projekt einfügen
+jsite.insert.description=Bitte warten Sie, während das Projekt eingefügt wird.
+jsite.insert.project-information=Projektinformationen
+jsite.insert.request-uri=Freesite
+jsite.insert.start-time=Beginn
+jsite.insert.progress=Fortschritt
+jsite.insert.insert-failed=<html><b>Einfügen fehlgeschlagen</b><br><br>Das Einfügen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingefügt werden konnten.</html>
+jsite.insert.insert-failed-with-cause=<html><b>Einfügen fehlgeschlagen</b><br><br>Das Einfügen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingefügt werden konnten.<br>Folgender Fehler trat auf:<br><br><code>{0}</code></html>
+jsite.insert.inserted=<html><b>Projekt eingefügt</b><br><br>Ihr Projekt wurde erfolgreich eingefügt.</html>
+
+jsite.file-scanner.can-not-read-directory=Kann Verzeichnis nicht lesen
+
+jsite.project.heading=Projekt auswählen
+jsite.project.description=Wählen Sie das Projekt aus, welches sie einfügen möchten, oder erstellen Sie ein neues Projekt.
+jsite.project.action.browse=Durchsuchen
+jsite.project.action.browse.choose=Auswählen
+jsite.project.action.browse.tooltip=Lokalen Pfad für Projekt auswählen
+jsite.project.action.add-project=Projekt erstellen
+jsite.project.action.add-project.tooltip=Ein neues Projekt erstellen
+jsite.project.new-project.name=Neues Projekt
+jsite.project.action.delete-project=Projekt löschen
+jsite.project.action.delete-project.tooltip=Ein Projekt löschen
+jsite.project.action.delete-project.confirm=<html><b>Löschung bestätigen</b><br><br>Das Projekt \u201e{0}\u201c wird gelöscht!<br>Möchten Sie fortfahren?</html>
+jsite.project.action.clone-project=Projekt duplizieren
+jsite.project.action.clone-project.copy=Kopie von {0}
+jsite.project.action.clone-project.tooltip=Das ausgewählte Projekt duplizieren
+jsite.project.project.information=Projektinformation
+jsite.project.project.name=Name
+jsite.project.project.description=Beschreibung
+jsite.project.project.local-path=Lokaler Pfad
+jsite.project.project.address=Adresse
+jsite.project.project.public-key=Anfrage-URI
+jsite.project.project.private-key=Einfüge-URI
+jsite.project.project.path=Pfad
+jsite.project.project.edition=Edition
+jsite.project.keygen.io-error=<html><b>Kommunikation fehlgeschlagen</b><br><br>Die Kommunikation mit dem Freenet Node<br>ergab folgende Fehlermeldung:<br><br><code>{0}</code><br><br>Bitte vergewissern Sie sich, dass der Node läuft und dass Sie<br> den korrekten Hostnamen und die korrekte Portnummer auf der<br>\u201eNode Einstellungen\u201c Seite eingegeben haben.</html>
+jsite.project.warning.no-local-path=<html><b>Kein lokaler Pfad</b><br><br>Sie haben keinen lokalen Pfad für die einzufügenden Dateien angegeben.<br>Es ist nicht möglich, ohne lokalen Pfad weiter zu machen.</html>
+jsite.project.warning.no-path=<html><b>Kein Freesite-Pfad</b><br><br>Sie haben keinen Pfad für die Freesite angegeben.<br>Es ist nicht möglich, ohne einen Freesite-Pfad weiter zu machen.</html>
+
+jsite.project-files.heading=Projektdateien
+jsite.project-files.description=<html>Auf dieser Seite können Parameter für die einzelnen Dateien dieses Projekts angegeben werden, z.B.<br>extern erstellte Schlüssel oder der korrekte MIME-Typ, wenn er nicht automatisch richtig erkannt wurde.</html> 
+jsite.project-files.action.rescan=Erneut einlesen
+jsite.project-files.action.rescan.tooltip=Die Liste mit Dateien dieses Projekts neu einlesen
+jsite.project-files.action.add-container=Hinzufügen
+jsite.project-files.action.add-container.tooltip=Erzeugt einen neuen Container und fügt diese Datei hinzu
+jsite.project-files.action.add-container.message=Bitte geben Sie den Namen des neuen Containers an 
+jsite.project-files.action.edit-container=Ändern
+jsite.project-files.action.edit-container.tooltip=Ändert den Namen des Containers
+jsite.project-files.action.edit-container.message=Bitte geben Sie den neuen Namen des Containers an
+jsite.project-files.action.delete-container=Löschen
+jsite.project-files.action.delete-container.tooltip=Löscht diesen Container
+jsite.project-files.action.delete-container.message=Wollen Sie diesen Container wirklich löschen?
+jsite.project-files.file-options=Dateioptionen
+jsite.project-files.default=Index-Datei
+jsite.project-files.default.tooltip=Lege Index-Datei für Projekt fest
+jsite.project-files.insert=Einfügen
+jsite.project-files.insert.tooltip=jSite fügt diese Datei ein
+jsite.project-files.custom-key=Extern erstellter Schlüssel
+jsite.project-files.custom-key.tooltip=Der extern erstellte Schlüssel für diese Datei
+jsite.project-files.mime-type=MIME-Typ
+jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier auswählen, wenn die automatische Erkennenung falsch ist
+jsite.project-files.container=Container
+jsite.project-files.container.tooltip=Wählt einen Container für diese Datei aus
+jsite.project-files.replacement=Ersetzungen
+jsite.project-files.replacement.tooltip=Aktiviert Ersetzungen in Datei
+jsite.project-files.replacement.edition-range=Reichweite
+jsite.project-files.replacement.edition-range.tooltip=Ersetzt auch $[EDITION+1], $[EDITION+2], usw.
+jsite.project-files.scan-error=<html><b>Fehler beim Einlesen der Dateien</b><br><br>Entweder existiert das Projektverzeichnis nicht,<br>oder einige Dateien und/oder Verzeichnisse sind nicht lesbar!<br>Bitte gehen Sie zurück und beheben Sie den Fehler!</html>
+jsite.project-files.empty-index=<html><b>Keine Index-Datei gewählt</b><br><br>Sie haben keine Index-Datei für das Projekt angegeben.<br>Obwohl es möglich ist, das zu machen, sollten Sie doch<br>eine Index-Datei angeben, um das Browsen zu erleichtern.</html>
+jsite.project-files.container-index=<html><b>Index-Datei in Container</b><br><br>Ihre Index-Datei befindet sich in einem Container! Das kann<br>dazu führen, dass Ihre Freesite von anderen Leuten gemieden wird.</html>
+jsite.project-files.index-not-html=<html><b>Index-Datei ist kein HTML</b><br><br>Ihre Index-Datei hat nicht den MIME-Typ "text/html"!<br>Das kann beim Besuch Ihrer Freesite zu<br>unerwarteten Ergebnissen führen.</html>
+jsite.project-files.no-node-running=<html><b>Der Node läuft nicht</b><br><br>Sie können das Projekt nicht einfügen, wenn<br>Ihr Node nicht läuft. Bitte starten Sie Ihren Node<br>und probieren Sie es erneut.</html> 
+jsite.project-files.no-custom-key=<html><b>Kein externer Schlüssel</b><br><br>Sie haben angegeben, dass die Datei <code>{0}</code><br>nicht eingefügt werden soll. Allerdings haben Sie<br>keinen extern erstellten Schlüssel angegeben.</html>
+jsite.project-files.no-node-selected=<html><b>Kein Node ausgewählt</b><br><br>Bitte wählen Sie einen Node aus dem Menü!</html>
diff --git a/src/de/todesbaum/jsite/main/CLI.java b/src/de/todesbaum/jsite/main/CLI.java
new file mode 100644 (file)
index 0000000..c401aaa
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * jSite - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.main;
+
+import java.io.PrintWriter;
+
+import de.todesbaum.jsite.application.EditionProject;
+import de.todesbaum.jsite.application.Freenet7Interface;
+import de.todesbaum.jsite.application.InsertListener;
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.application.ProjectInserter;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: CLI.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public class CLI implements InsertListener {
+
+       private Object lockObject = new Object();
+       private PrintWriter outputWriter = new PrintWriter(System.out, true);
+       private Freenet7Interface freenetInterface;
+       private ProjectInserter projectInserter = new ProjectInserter();
+       private Node[] nodes;
+       private Project[] projects;
+       private boolean finished = false;
+       private boolean success;
+
+       private CLI(String[] args) {
+
+               if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) {
+                       outputWriter.println("\nParameters:\n");
+                       outputWriter.println("  --node=<node name>");
+                       outputWriter.println("  --project=<project name>");
+                       outputWriter.println("  --local-directory=<local directory>");
+                       outputWriter.println("  --path=<path>");
+                       outputWriter.println("  --edition=<edition>");
+                       outputWriter.println("\nA project gets inserted when a new project is loaded on the command line,");
+                       outputWriter.println("or when the command line is finished. --local-directory, --path, and --edition");
+                       outputWriter.println("override the parameters in the project.");
+                       return;
+               }
+
+               Configuration configuration = new Configuration();
+               if (!configuration.createLockFile()) {
+                       outputWriter.println("Lock file found!");
+                       return;
+               }
+
+               projectInserter.addInsertListener(this);
+               projects = configuration.getProjects();
+               Node node = configuration.getSelectedNode();
+               nodes = configuration.getNodes();
+
+               freenetInterface = new Freenet7Interface();
+               freenetInterface.setNode(node);
+
+               projectInserter.setFreenetInterface(freenetInterface);
+
+               Project currentProject = null;
+               for (int argumentIndex = 0, argumentSize = args.length; argumentIndex < argumentSize; argumentIndex++) {
+                       String argument = args[argumentIndex];
+                       String value = argument.substring(argument.indexOf('=') + 1).trim();
+                       if (argument.startsWith("--node=")) {
+                               Node newNode = getNode(value);
+                               if (newNode == null) {
+                                       outputWriter.println("Node \"" + value + "\" not found.");
+                                       return;
+                               }
+                               node = newNode;
+                               freenetInterface.setNode(node);
+                       } else if (argument.startsWith("--project=")) {
+                               if (currentProject != null) {
+                                       if (insertProject(currentProject)) {
+                                               outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted.");
+                                       } else {
+                                               outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted.");
+                                       }
+                                       currentProject = null;
+                               }
+                               currentProject = getProject(value);
+                               if (currentProject == null) {
+                                       outputWriter.println("Project \"" + value + "\" not found.");
+                               }
+                       } else if (argument.startsWith("--local-directory")) {
+                               if (currentProject == null) {
+                                       outputWriter.println("You can't specifiy --local-directory before --project.");
+                                       return;
+                               }
+                               currentProject.setLocalPath(value);
+                       } else if (argument.startsWith("--path=")) {
+                               if (currentProject == null) {
+                                       outputWriter.println("You can't specify --path before --project.");
+                                       return;
+                               }
+                               currentProject.setPath(value);
+                       } else if (argument.startsWith("--edition=")) {
+                               if (currentProject == null) {
+                                       outputWriter.println("You can't specify --edition before --project.");
+                                       return;
+                               }
+                               if (currentProject instanceof EditionProject) {
+                                       ((EditionProject) currentProject).setEdition(Integer.parseInt(value));
+                               } else {
+                                       outputWriter.println("Project \"" + currentProject.getName() + "\" is not an edition-based project.");
+                                       return;
+                               }
+                       } else {
+                               outputWriter.println("Unknown parameter: " + argument);
+                               return;
+                       }
+               }
+
+               if (currentProject != null) {
+                       if (insertProject(currentProject)) {
+                               outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted.");
+                       } else {
+                               outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted.");
+                       }
+               }
+
+               configuration.setProjects(projects);
+               configuration.save();
+       }
+
+       private Project getProject(String name) {
+               for (Project project: projects) {
+                       if (project.getName().equals(name)) {
+                               return project;
+                       }
+               }
+               return null;
+       }
+
+       private Node getNode(String name) {
+               for (Node node: nodes) {
+                       if (node.getName().equals(name)) {
+                               return node;
+                       }
+               }
+               return null;
+       }
+
+       private boolean insertProject(Project currentProject) {
+               if (!freenetInterface.hasNode()) {
+                       outputWriter.println("Node is not running!");
+                       return false;
+               }
+               projectInserter.setProject(currentProject);
+               projectInserter.start();
+               synchronized (lockObject) {
+                       while (!finished) {
+                               try {
+                                       lockObject.wait();
+                               } catch (InterruptedException e) {
+                               }
+                       }
+               }
+               return success;
+       }
+
+       //
+       // INTERFACE InsertListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertStarted(Project project) {
+               outputWriter.println("Starting Insert of project \"" + project.getName() + "\".");
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized) {
+               outputWriter.println("Progress: " + succeeded + " done, " + failed + " failed, " + fatal + " fatal, " + total + " total" + (finalized ? " (finalized)" : "") + ", " + ((succeeded + failed + fatal) * 100 / total) + "%");
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertFinished(Project project, boolean success, Throwable cause) {
+               outputWriter.println("Request URI: " + project.getFinalURI(0));
+               finished = true;
+               if (success) {
+                       if (project instanceof EditionProject) {
+                               ((EditionProject) project).setEdition(((EditionProject) project).getEdition() + 1);
+                       }
+               }
+               this.success = success;
+               synchronized (lockObject) {
+                       lockObject.notify();
+               }
+       }
+
+       //
+       // MAIN
+       //
+
+       public static void main(String[] args) {
+               new CLI(args);
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/main/Configuration.java b/src/de/todesbaum/jsite/main/Configuration.java
new file mode 100644 (file)
index 0000000..3931dcb
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.main;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import de.todesbaum.jsite.application.EditionProject;
+import de.todesbaum.jsite.application.FileOption;
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.util.io.StreamCopier;
+import de.todesbaum.util.xml.SimpleXML;
+import de.todesbaum.util.xml.XML;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Configuration.java 418 2006-03-29 17:49:16Z bombe $
+ */
+public class Configuration {
+
+       private String filename;
+       private String lockFilename;
+       private SimpleXML rootNode;
+
+       public Configuration() {
+               filename = System.getProperty("user.home") + "/.jSite/config7";
+               lockFilename = System.getProperty("user.home") + "/.jSite/lock7";
+               readConfiguration();
+       }
+       
+       private boolean createConfigDirectory() {
+               File configDirectory = new File(System.getProperty("user.home"), ".jSite");
+               return (configDirectory.exists() && configDirectory.isDirectory()) || configDirectory.mkdirs();
+       }
+
+       public boolean createLockFile() {
+               if (!createConfigDirectory()) {
+                       return false;
+               }
+               File lockFile = new File(lockFilename);
+               lockFile.deleteOnExit();
+               try {
+                       return lockFile.createNewFile();
+               } catch (IOException e) {
+               }
+               return false;
+       }
+
+       private void readConfiguration() {
+               File configurationFile = new File(filename);
+               if (configurationFile.exists()) {
+                       ByteArrayOutputStream fileByteOutputStream = null;
+                       FileInputStream fileInputStream = null;
+                       try {
+                               fileByteOutputStream = new ByteArrayOutputStream();
+                               fileInputStream = new FileInputStream(configurationFile);
+                               StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length());
+                               fileByteOutputStream.close();
+                               byte[] fileBytes = fileByteOutputStream.toByteArray();
+                               rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes));
+                               return;
+                       } catch (FileNotFoundException e) {
+                       } catch (IOException e) {
+                       } finally {
+                               if (fileInputStream != null) {
+                                       try {
+                                               fileInputStream.close();
+                                       } catch (IOException ioe1) {
+                                       }
+                               }
+                               if (fileByteOutputStream != null) {
+                                       try {
+                                               fileByteOutputStream.close();
+                                       } catch (IOException ioe1) {
+                                       }
+                               }
+                       }
+               }
+               rootNode = new SimpleXML("configuration");
+       }
+
+       public boolean save() {
+               File configurationFile = new File(filename);
+               if (!configurationFile.exists()) {
+                       File configurationFilePath = configurationFile.getParentFile();
+                       if (!configurationFilePath.exists() && !configurationFilePath.mkdirs()) {
+                               return false;
+                       }
+               }
+               FileOutputStream fileOutputStream = null;
+               ByteArrayInputStream configurationInputStream = null;
+               try {
+                       byte[] configurationBytes = XML.transformToByteArray(rootNode.getDocument());
+                       configurationInputStream = new ByteArrayInputStream(configurationBytes);
+                       fileOutputStream = new FileOutputStream(configurationFile);
+                       StreamCopier.copy(configurationInputStream, fileOutputStream, configurationBytes.length);
+                       return true;
+               } catch (IOException ioe1) {
+               } finally {
+                       if (configurationInputStream != null) {
+                               try {
+                                       configurationInputStream.close();
+                               } catch (IOException ioe1) {
+                               }
+                       }
+                       if (fileOutputStream != null) {
+                               try {
+                                       fileOutputStream.close();
+                               } catch (IOException ioe1) {
+                               }
+                       }
+               }
+               return false;
+       }
+
+       private String getNodeValue(String[] nodeNames, String defaultValue) {
+               SimpleXML node = rootNode;
+               int nodeIndex = 0;
+               while ((node != null) && (nodeIndex < nodeNames.length)) {
+                       node = node.getNode(nodeNames[nodeIndex++]);
+               }
+               if (node == null) {
+                       return defaultValue;
+               }
+               return node.getValue();
+       }
+
+       private int getNodeIntValue(String[] nodeNames, int defaultValue) {
+               try {
+                       return Integer.parseInt(getNodeValue(nodeNames, String.valueOf(defaultValue)));
+               } catch (NumberFormatException nfe1) {
+               }
+               return defaultValue;
+       }
+
+       private boolean getNodeBooleanValue(String[] nodeNames, boolean defaultValue) {
+               String nodeValue = getNodeValue(nodeNames, null);
+               if (nodeValue == null) {
+                       return defaultValue;
+               }
+               return Boolean.parseBoolean(nodeValue);
+       }
+
+       public String getNodeAddress() {
+               return getNodeValue(new String[] { "node-address" }, "localhost");
+       }
+
+       public void setNodeAddress(String nodeAddress) {
+               rootNode.replace("node-address", nodeAddress);
+       }
+
+       public int getNodePort() {
+               return getNodeIntValue(new String[] { "node-port" }, 9481);
+       }
+
+       public void setNodePort(int nodePort) {
+               rootNode.replace("node-port", String.valueOf(nodePort));
+       }
+
+       public boolean isSkipNodePage() {
+               return getNodeBooleanValue(new String[] { "skip-node-page" }, false);
+       }
+
+       public void setSkipNodePage(boolean skipNodePage) {
+               rootNode.replace("skip-node-page", String.valueOf(skipNodePage));
+       }
+
+       public Project[] getProjects() {
+               List<Project> projects = new ArrayList<Project>();
+               SimpleXML projectsNode = rootNode.getNode("project-list");
+               if (projectsNode != null) {
+                       SimpleXML[] projectNodes = projectsNode.getNodes("project");
+                       for (SimpleXML projectNode: projectNodes) {
+                               try {
+                                       Project project = null;
+                                       SimpleXML typeNode = projectNode.getNode("type");
+                                       if ("edition".equals(typeNode.getValue())) {
+                                               EditionProject editionProject = new EditionProject();
+                                               project = editionProject;
+                                               editionProject.setEdition(Integer.parseInt(projectNode.getNode("edition").getValue()));
+                                       }
+                                       projects.add(project);
+                                       project.setDescription(projectNode.getNode("description").getValue());
+                                       project.setIndexFile(projectNode.getNode("index-file").getValue());
+                                       project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue()));
+                                       project.setLocalPath(projectNode.getNode("local-path").getValue());
+                                       project.setName(projectNode.getNode("name").getValue());
+                                       project.setPath(projectNode.getNode("path").getValue());
+                                       project.setInsertURI(projectNode.getNode("insert-uri").getValue());
+                                       project.setRequestURI(projectNode.getNode("request-uri").getValue());
+                                       SimpleXML fileOptionsNode = projectNode.getNode("file-options");
+                                       Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
+                                       if (fileOptionsNode != null) {
+                                               SimpleXML[] fileOptionNodes = fileOptionsNode.getNodes("file-option");
+                                               for (SimpleXML fileOptionNode: fileOptionNodes) {
+                                                       String filename = fileOptionNode.getNode("filename").getValue();
+                                                       FileOption fileOption = project.getFileOption(filename);
+                                                       fileOption.setInsert(Boolean.parseBoolean(fileOptionNode.getNode("insert").getValue()));
+                                                       fileOption.setCustomKey(fileOptionNode.getNode("custom-key").getValue());
+                                                       fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue());
+                                                       fileOption.setContainer(fileOptionNode.getNode("container").getValue());
+                                                       if (fileOptionNode.getNode("replace-edition") != null) {
+                                                               fileOption.setReplaceEdition(Boolean.parseBoolean(fileOptionNode.getNode("replace-edition").getValue()));
+                                                               fileOption.setEditionRange(Integer.parseInt(fileOptionNode.getNode("edition-range").getValue()));
+                                                       }
+                                                       fileOptions.put(filename, fileOption);
+                                               }
+                                       }
+                                       project.setFileOptions(fileOptions);
+                               } catch (NumberFormatException nfe1) {
+                                       nfe1.printStackTrace();
+                               }
+                       }
+               }
+               return projects.toArray(new Project[projects.size()]);
+       }
+
+       public void setProjects(Project[] projects) {
+               SimpleXML projectsNode = new SimpleXML("project-list");
+               for (Project project: projects) {
+                       SimpleXML projectNode = projectsNode.append("project");
+                       if (project instanceof EditionProject) {
+                               projectNode.append("type", "edition");
+                               projectNode.append("edition", String.valueOf(((EditionProject) project).getEdition()));
+                       }
+                       projectNode.append("description", project.getDescription());
+                       projectNode.append("index-file", project.getIndexFile());
+                       projectNode.append("last-insertion-time", String.valueOf(project.getLastInsertionTime()));
+                       projectNode.append("local-path", project.getLocalPath());
+                       projectNode.append("name", project.getName());
+                       projectNode.append("path", project.getPath());
+                       projectNode.append("insert-uri", project.getInsertURI());
+                       projectNode.append("request-uri", project.getRequestURI());
+                       SimpleXML fileOptionsNode = projectNode.append("file-options");
+                       Iterator<Entry<String, FileOption>> entries = project.getFileOptions().entrySet().iterator();
+                       while (entries.hasNext()) {
+                               Entry<String, FileOption> entry = entries.next();
+                               FileOption fileOption = entry.getValue();
+                               if (fileOption.isCustom()) {
+                                       SimpleXML fileOptionNode = fileOptionsNode.append("file-option");
+                                       fileOptionNode.append("filename", entry.getKey());
+                                       fileOptionNode.append("insert", String.valueOf(fileOption.isInsert()));
+                                       fileOptionNode.append("custom-key", fileOption.getCustomKey());
+                                       fileOptionNode.append("mime-type", fileOption.getMimeType());
+                                       fileOptionNode.append("container", fileOption.getContainer());
+                                       fileOptionNode.append("replace-edition", String.valueOf(fileOption.getReplaceEdition()));
+                                       fileOptionNode.append("edition-range", String.valueOf(fileOption.getEditionRange()));
+                               }
+                       }
+               }
+               rootNode.replace(projectsNode);
+       }
+
+       public Locale getLocale() {
+               String language = getNodeValue(new String[] { "i18n", "language" }, "en");
+               String country = getNodeValue(new String[] { "i18n", "country" }, null);
+               if (country != null) {
+                       return new Locale(language, country);
+               }
+               return new Locale(language);
+       }
+
+       public void setLocale(Locale locale) {
+               SimpleXML i18nNode = new SimpleXML("i18n");
+               if (locale.getCountry().length() != 0) {
+                       i18nNode.append("country", locale.getCountry());
+               }
+               i18nNode.append("language", locale.getLanguage());
+               rootNode.replace(i18nNode);
+               return;
+       }
+       
+       public Node[] getNodes() {
+               SimpleXML nodesNode = rootNode.getNode("nodes");
+               if (nodesNode == null) {
+                       String hostname = getNodeAddress();
+                       int port = getNodePort();
+                       return new Node[] { new Node(hostname, port, "Node") };
+               }
+               SimpleXML[] nodeNodes = nodesNode.getNodes("node");
+               Node[] returnNodes = new Node[nodeNodes.length];
+               int nodeIndex = 0;
+               for (SimpleXML nodeNode: nodeNodes) {
+                       String name = nodeNode.getNode("name").getValue();
+                       String hostname = nodeNode.getNode("hostname").getValue();
+                       int port = Integer.parseInt(nodeNode.getNode("port").getValue());
+                       Node node = new Node(hostname, port, name);
+                       returnNodes[nodeIndex++] = node;
+               }
+               return returnNodes;
+       }
+       
+       public void setNodes(Node[] nodes) {
+               SimpleXML nodesNode = new SimpleXML("nodes");
+               for (Node node: nodes) {
+                       SimpleXML nodeNode = nodesNode.append("node");
+                       nodeNode.append("name", node.getName());
+                       nodeNode.append("hostname", node.getHostname());
+                       nodeNode.append("port", String.valueOf(node.getPort()));
+               }
+               rootNode.replace(nodesNode);
+               rootNode.remove("node-address");
+               rootNode.remove("node-port");
+       }
+
+       public void setSelectedNode(Node selectedNode) {
+               SimpleXML selectedNodeNode = new SimpleXML("selected-node");
+               selectedNodeNode.append("name", selectedNode.getName());
+               selectedNodeNode.append("hostname", selectedNode.getHostname());
+               selectedNodeNode.append("port", String.valueOf(selectedNode.getPort()));
+               rootNode.replace(selectedNodeNode);
+       }
+       
+       public Node getSelectedNode() {
+               SimpleXML selectedNodeNode = rootNode.getNode("selected-node");
+               if (selectedNodeNode == null) {
+                       return null;
+               }
+               String name = selectedNodeNode.getNode("name").getValue();
+               String hostname = selectedNodeNode.getNode("hostname").getValue();
+               int port = Integer.valueOf(selectedNodeNode.getNode("port").getValue());
+               return new Node(hostname, port, name);
+       }
+       
+}
diff --git a/src/de/todesbaum/jsite/main/Main.java b/src/de/todesbaum/jsite/main/Main.java
new file mode 100644 (file)
index 0000000..4891f8f
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.main;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ButtonGroup;
+import javax.swing.Icon;
+import javax.swing.JList;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import de.todesbaum.jsite.application.FileOption;
+import de.todesbaum.jsite.application.Freenet7Interface;
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.gui.NodeManagerListener;
+import de.todesbaum.jsite.gui.NodeManagerPage;
+import de.todesbaum.jsite.gui.ProjectFilesPage;
+import de.todesbaum.jsite.gui.ProjectInsertPage;
+import de.todesbaum.jsite.gui.ProjectPage;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.image.IconLoader;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+import de.todesbaum.util.swing.WizardListener;
+
+/**
+ * @author <a href="mailto:droden@gmail.com">David Roden </a>
+ * @version $Id: Main.java 456 2006-04-03 17:54:44Z bombe $
+ */
+public class Main implements ActionListener, ListSelectionListener, WizardListener, NodeManagerListener {
+
+       private static boolean debug = false;
+       private Configuration configuration = new Configuration();
+       private Freenet7Interface freenetInterface = new Freenet7Interface();
+       protected Icon jSiteIcon;
+
+       private static enum PageType {
+               PAGE_NODE_MANAGER, PAGE_PROJECTS, PAGE_PROJECT_FILES, PAGE_INSERT_PROJECT
+       }
+
+       private static final Locale[] SUPPORTED_LOCALES = new Locale[] { Locale.ENGLISH, Locale.GERMAN };
+       private Map<Locale, Action> languageActions = new HashMap<Locale, Action>();
+       private Action manageNodeAction;
+       private Action aboutAction;
+       protected TWizard wizard;
+       protected JMenu nodeMenu;
+       private Node selectedNode;
+       private final Map<PageType, TWizardPage> pages = new HashMap<PageType, TWizardPage>();
+
+       private Main() {
+               Locale.setDefault(configuration.getLocale());
+               I18n.setLocale(configuration.getLocale());
+               if (!configuration.createLockFile()) {
+                       JOptionPane.showMessageDialog(null, I18n.getMessage("jsite.main.already-running"), null, JOptionPane.ERROR_MESSAGE);
+                       return;
+               }
+               wizard = new TWizard();
+               createActions();
+               wizard.setJMenuBar(createMenuBar());
+               wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+               wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+               wizard.setPreviousEnabled(false);
+               wizard.setNextEnabled(true);
+               wizard.addWizardListener(this);
+               jSiteIcon = IconLoader.loadIcon("/jsite-icon.png");
+               wizard.setIcon(jSiteIcon);
+
+               initPages();
+               showPage(PageType.PAGE_PROJECTS);
+               wizard.setPreviousName((String) manageNodeAction.getValue(Action.NAME));
+       }
+
+       private void createActions() {
+               for (final Locale locale: SUPPORTED_LOCALES) {
+                       languageActions.put(locale, new AbstractAction(I18n.getMessage("jsite.menu.language." + locale.getLanguage())) {
+
+                               public void actionPerformed(ActionEvent actionEvent) {
+                                       switchLanguage(locale);
+                               }
+                       });
+               }
+               manageNodeAction = new AbstractAction(I18n.getMessage("jsite.menu.nodes.manage-nodes")) {
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               showPage(PageType.PAGE_NODE_MANAGER);
+                       }
+               };
+               aboutAction = new AbstractAction(I18n.getMessage("jsite.menu.help.about")) {
+
+                       public void actionPerformed(ActionEvent e) {
+                               JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.about.message"), Version.getVersion()), null, JOptionPane.INFORMATION_MESSAGE, jSiteIcon);
+                       }
+               };
+       }
+
+       private JMenuBar createMenuBar() {
+               JMenuBar menuBar = new JMenuBar();
+               JMenu languageMenu = new JMenu(I18n.getMessage("jsite.menu.languages"));
+               menuBar.add(languageMenu);
+               ButtonGroup languageButtonGroup = new ButtonGroup();
+               for (Locale locale: SUPPORTED_LOCALES) {
+                       Action languageAction = languageActions.get(locale);
+                       JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(languageActions.get(locale));
+                       if (locale.equals(Locale.getDefault())) {
+                               menuItem.setSelected(true);
+                       }
+                       languageAction.putValue("menuItem", menuItem);
+                       languageButtonGroup.add(menuItem);
+                       languageMenu.add(menuItem);
+               }
+               nodeMenu = new JMenu(I18n.getMessage("jsite.menu.nodes"));
+               menuBar.add(nodeMenu);
+               selectedNode = configuration.getSelectedNode();
+               nodesUpdated(configuration.getNodes());
+
+               /* evil hack to right-align the help menu */
+               JPanel panel = new JPanel();
+               panel.setOpaque(false);
+               menuBar.add(panel);
+
+               JMenu helpMenu = new JMenu(I18n.getMessage("jsite.menu.help"));
+               menuBar.add(helpMenu);
+               helpMenu.add(aboutAction);
+               return menuBar;
+       }
+
+       private void initPages() {
+               NodeManagerPage nodeManagerPage = new NodeManagerPage();
+               nodeManagerPage.setName("page.node-manager");
+               nodeManagerPage.addNodeManagerListener(this);
+               nodeManagerPage.setNodes(configuration.getNodes());
+               pages.put(PageType.PAGE_NODE_MANAGER, nodeManagerPage);
+
+               ProjectPage projectPage = new ProjectPage();
+               projectPage.setName("page.project");
+               projectPage.setProjects(configuration.getProjects());
+               projectPage.setFreenetInterface(freenetInterface);
+               projectPage.addListSelectionListener(this);
+               pages.put(PageType.PAGE_PROJECTS, projectPage);
+
+               ProjectFilesPage projectFilesPage = new ProjectFilesPage();
+               projectFilesPage.setName("page.project.files");
+               pages.put(PageType.PAGE_PROJECT_FILES, projectFilesPage);
+
+               ProjectInsertPage projectInsertPage = new ProjectInsertPage();
+               projectInsertPage.setDebug(debug);
+               projectInsertPage.setName("page.project.insert");
+               projectInsertPage.setFreenetInterface(freenetInterface);
+               pages.put(PageType.PAGE_INSERT_PROJECT, projectInsertPage);
+       }
+
+       protected void showPage(PageType pageType) {
+               wizard.setPreviousEnabled(pageType.ordinal() > 0);
+               wizard.setNextEnabled(pageType.ordinal() < (pages.size() - 1));
+               wizard.setPage(pages.get(pageType));
+               wizard.setTitle(pages.get(pageType).getHeading() + " - jSite");
+       }
+
+       private boolean saveConfiguration() {
+               NodeManagerPage nodeManagerPage = (NodeManagerPage) pages.get(PageType.PAGE_NODE_MANAGER);
+               configuration.setNodes(nodeManagerPage.getNodes());
+               if (selectedNode != null) {
+                       configuration.setSelectedNode(selectedNode);
+               }
+
+               ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS);
+               configuration.setProjects(projectPage.getProjects());
+
+               return configuration.save();
+       }
+
+       private Locale findSupportedLocale(Locale forLocale) {
+               for (Locale locale: SUPPORTED_LOCALES) {
+                       if (locale.equals(forLocale)) {
+                               return locale;
+                       }
+               }
+               for (Locale locale: SUPPORTED_LOCALES) {
+                       if (locale.getCountry().equals(forLocale.getCountry()) && locale.getLanguage().equals(forLocale.getLanguage())) {
+                               return locale;
+                       }
+               }
+               for (Locale locale: SUPPORTED_LOCALES) {
+                       if (locale.getLanguage().equals(forLocale.getLanguage())) {
+                               return locale;
+                       }
+               }
+               return SUPPORTED_LOCALES[0];
+       }
+
+       //
+       // ACTIONS
+       //
+
+       protected void switchLanguage(Locale locale) {
+               Locale supportedLocale = findSupportedLocale(locale);
+               Action languageAction = languageActions.get(supportedLocale);
+               JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) languageAction.getValue("menuItem");
+               menuItem.setSelected(true);
+               /* show the restart message in the other language! */
+               Locale currentLocale = I18n.getLocale();
+               I18n.setLocale(supportedLocale);
+               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.menu.language.change.restart-message"), null, JOptionPane.INFORMATION_MESSAGE);
+               I18n.setLocale(currentLocale);
+               configuration.setLocale(supportedLocale);
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void valueChanged(ListSelectionEvent e) {
+               JList list = (JList) e.getSource();
+               int selectedRow = list.getSelectedIndex();
+               wizard.setNextEnabled(selectedRow > -1);
+       }
+
+       //
+       // INTERFACE WizardListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void wizardNextPressed(TWizard wizard) {
+               String pageName = wizard.getPage().getName();
+               if ("page.node-manager".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECTS);
+                       wizard.setPreviousName((String) manageNodeAction.getValue(Action.NAME));
+               } else if ("page.project".equals(pageName)) {
+                       ProjectPage projectPage = (ProjectPage) wizard.getPage();
+                       Project project = projectPage.getSelectedProject();
+                       if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.no-local-path"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.no-path"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       ((ProjectFilesPage) pages.get(PageType.PAGE_PROJECT_FILES)).setProject(project);
+                       ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).setProject(project);
+                       showPage(PageType.PAGE_PROJECT_FILES);
+                       wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               } else if ("page.project.files".equals(pageName)) {
+                       ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS);
+                       Project project = projectPage.getSelectedProject();
+                       if (selectedNode == null) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.no-node-selected"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       if (project.getIndexFile() == null) {
+                               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.project-files.empty-index"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
+                                       return;
+                               }
+                       }
+                       if (!project.getFileOption(project.getIndexFile()).getContainer().equals("")) {
+                               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.project-files.container-index"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
+                                       return;
+                               }
+                       }
+                       if (!project.getFileOption(project.getIndexFile()).getMimeType().equals("text/html")) {
+                               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.project-files.index-not-html"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
+                                       return;
+                               }
+                       }
+                       Map<String, FileOption> fileOptions = project.getFileOptions();
+                       Set<Entry<String, FileOption>> fileOptionEntries = fileOptions.entrySet();
+                       for (Entry<String, FileOption> fileOptionEntry: fileOptionEntries) {
+                               FileOption fileOption = fileOptionEntry.getValue();
+                               if (!fileOption.isInsert() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(fileOption.getCustomKey()))) {
+                                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.project-files.no-custom-key"), fileOptionEntry.getKey()), null, JOptionPane.ERROR_MESSAGE);
+                                       return;
+                               }
+                       }
+                       boolean nodeRunning = false;
+                       try {
+                               nodeRunning = freenetInterface.isNodePresent();
+                       } catch (IOException e) {
+                       }
+                       if (!nodeRunning) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.no-node-running"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       showPage(PageType.PAGE_INSERT_PROJECT);
+                       nodeMenu.setEnabled(false);
+               } else if ("page.project.insert".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECTS);
+                       nodeMenu.setEnabled(true);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void wizardPreviousPressed(TWizard wizard) {
+               String pageName = wizard.getPage().getName();
+               if ("page.project".equals(pageName)) {
+                       showPage(PageType.PAGE_NODE_MANAGER);
+                       wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               } else if ("page.project.files".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECTS);
+                       wizard.setPreviousName((String) manageNodeAction.getValue(Action.NAME));
+               } else if ("page.project.insert".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECT_FILES);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void wizardQuitPressed(TWizard wizard) {
+               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
+                       if (saveConfiguration()) {
+                               System.exit(0);
+                       }
+                       if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.config-not-saved"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) {
+                               System.exit(0);
+                       }
+               }
+       }
+
+       //
+       // INTERFACE NodeManagerListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void nodesUpdated(Node[] nodes) {
+               nodeMenu.removeAll();
+               ButtonGroup nodeButtonGroup = new ButtonGroup();
+               Node newSelectedNode = null;
+               for (Node node: nodes) {
+                       JRadioButtonMenuItem nodeMenuItem = new JRadioButtonMenuItem(node.getName());
+                       nodeMenuItem.putClientProperty("Node", node);
+                       nodeMenuItem.addActionListener(this);
+                       nodeButtonGroup.add(nodeMenuItem);
+                       if (node.equals(selectedNode)) {
+                               newSelectedNode = node;
+                               nodeMenuItem.setSelected(true);
+                       }
+                       nodeMenu.add(nodeMenuItem);
+               }
+               nodeMenu.addSeparator();
+               nodeMenu.add(manageNodeAction);
+               selectedNode = newSelectedNode;
+               freenetInterface.setNode(selectedNode);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void actionPerformed(ActionEvent e) {
+               Object source = e.getSource();
+               if (source instanceof JRadioButtonMenuItem) {
+                       JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) source;
+                       Node node = (Node) menuItem.getClientProperty("Node");
+                       selectedNode = node;
+                       freenetInterface.setNode(selectedNode);
+               }
+       }
+
+       //
+       // MAIN METHOD
+       //
+
+       public static void main(String[] args) {
+               System.setProperty("swing.plaf.metal.userFont", "Tahoma");
+               System.setProperty("swing.plaf.metal.controlFont", "Tahoma");
+               System.setProperty("swing.aatext", "true");
+               debug = (args.length > 0) && (args[0].equals("--debug"));
+               new Main();
+       }
+
+}
diff --git a/src/de/todesbaum/jsite/main/Version.java b/src/de/todesbaum/jsite/main/Version.java
new file mode 100644 (file)
index 0000000..2192062
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * jSite - a tool for uploading websites into Freenet
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.jsite.main;
+
+/**
+ * @author <a href="mailto:droden@gmail.com">David Roden </a>
+ * @version $Id: Version.java 457 2006-04-03 22:08:35Z bombe $
+ */
+public class Version {
+
+       private static final String VERSION = "0.4";
+
+       public static final String getVersion() {
+               return VERSION;
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Client.java b/src/de/todesbaum/util/freenet/fcp2/Client.java
new file mode 100644 (file)
index 0000000..e904a26
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Client executes {@link Command}s over a {@link Connection} to a
+ * {@link Node} and delivers resulting {@link Message}s.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Client.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public class Client implements ConnectionListener {
+
+       /** The connection this client operates on. */
+       private final Connection connection;
+
+       /** The identifiers the client filters messages for. */
+       private List<String> identifiers = new ArrayList<String>();
+
+       /** The queued messages. */
+       private final List<Message> messageQueue = new ArrayList<Message>();
+
+       /** Whether the client was disconnected. */
+       private boolean disconnected = false;
+
+       /** Whether to catch all messages from the connection. */
+       private boolean catchAll = false;
+
+       /**
+        * Creates a new client that operates on the specified connection.
+        * 
+        * @param connection
+        *            The connection to operate on
+        */
+       public Client(Connection connection) {
+               this.connection = connection;
+               connection.addConnectionListener(this);
+       }
+
+       /**
+        * Creates a new client that operates on the specified connection and
+        * immediately executes the specified command.
+        * 
+        * @param connection
+        *            The connection to operate on
+        * @param command
+        *            The command to execute
+        * @throws IOException
+        *             if an I/O error occurs
+        * @see #execute(Command)
+        */
+       public Client(Connection connection, Command command) throws IOException {
+               this(connection);
+               execute(command);
+       }
+
+       /**
+        * Returns whether this client catches all messages going over the
+        * connection.
+        * 
+        * @return <code>true</code> if the client catches all messages,
+        *         <code>false</code> otherwise
+        */
+       public boolean isCatchAll() {
+               return catchAll;
+       }
+
+       /**
+        * Sets whether this client catches all messages going over the connection.
+        * 
+        * @param catchAll
+        *            <code>true</code> if the client should catch all messages,
+        *            <code>false</code> otherwise
+        */
+       public void setCatchAll(boolean catchAll) {
+               this.catchAll = catchAll;
+       }
+
+       /**
+        * Executes the specified command. This will also clear the queue of
+        * messages, discarding all messages that resulted from the previous command
+        * and have not yet been read.
+        * 
+        * @param command
+        *            The command to execute
+        * @throws IOException
+        *             if an I/O error occurs
+        * @see #execute(Command, boolean)
+        */
+       public void execute(Command command) throws IOException {
+               execute(command, true);
+       }
+
+       /**
+        * Executes the specified command and optionally clears the list of
+        * identifiers this clients listens to before starting the command.
+        * 
+        * @param command
+        *            The command to execute
+        * @param removeExistingIdentifiers
+        *            If <code>true</code>, the list of identifiers that this
+        *            clients listens to is cleared
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void execute(Command command, boolean removeExistingIdentifiers) throws IOException {
+               synchronized (messageQueue) {
+                       messageQueue.clear();
+                       if (removeExistingIdentifiers) {
+                               identifiers.clear();
+                       }
+                       identifiers.add(command.getIdentifier());
+               }
+               connection.execute(command);
+       }
+
+       /**
+        * Returns the next message, waiting endlessly for it, if need be. If you
+        * are not sure whether a message will arrive, better use
+        * {@link #readMessage(long)} to only wait for a specific time.
+        * 
+        * @return The next message that resulted from the execution of the last
+        *         command
+        * @see #readMessage(long)
+        * @see #execute(Command)
+        */
+       public Message readMessage() {
+               return readMessage(0);
+       }
+
+       /**
+        * Returns the next message. If the message queue is currently empty, at
+        * least <code>maxWaitTime</code> milliseconds will be waited for a
+        * message to arrive.
+        * 
+        * @param maxWaitTime
+        *            The minimum time to wait for a message, in milliseconds
+        * @return The message, or <code>null</code> if no message arrived in time
+        *         or the client is currently disconnected
+        * @see #isDisconnected()
+        * @see Object#wait(long)
+        */
+       public Message readMessage(long maxWaitTime) {
+               synchronized (messageQueue) {
+                       if (disconnected) {
+                               return null;
+                       }
+                       if (messageQueue.size() == 0) {
+                               try {
+                                       messageQueue.wait(maxWaitTime);
+                               } catch (InterruptedException ie1) {
+                               }
+                       }
+                       if (messageQueue.size() > 0) {
+                               return messageQueue.remove(0);
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns whether the client is currently disconnected.
+        * 
+        * @return <code>true</code> if the client is disconnected,
+        *         <code>false</code> otherwise
+        */
+       public boolean isDisconnected() {
+               synchronized (messageQueue) {
+                       return disconnected;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void messageReceived(Connection connection, Message message) {
+               synchronized (messageQueue) {
+                       if (catchAll || (message.getIdentifier().length() == 0) || identifiers.contains(message.getIdentifier())) {
+                               messageQueue.add(message);
+                               messageQueue.notify();
+                       }
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void connectionTerminated(Connection connection) {
+               synchronized (messageQueue) {
+                       disconnected = true;
+                       messageQueue.notify();
+               }
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/de/todesbaum/util/freenet/fcp2/ClientHello.java
new file mode 100644 (file)
index 0000000..a482345
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Implementation of the <code>ClientHello</code> command. This command must
+ * be sent as the first command on a connection ({@link de.todesbaum.util.freenet.fcp2.Connection#connect()}
+ * takes care of that) and must not be sent afterwards.
+ * <p>
+ * The node can answer with the following messages: <code>NodeHello</code>.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ClientHello.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public class ClientHello extends Command {
+
+       /** The name of the client. */
+       protected String name;
+
+       /** The version of the FCP protocol the client expects. */
+       protected String expectedVersion = "2.0";
+
+       /**
+        * Creates a new <code>ClientHello</code> command.
+        */
+       public ClientHello() {
+               super("ClientHello", "ClientHello-" + System.currentTimeMillis());
+       }
+
+       /**
+        * Returns the value of the <code>ExpectedVersion</code> parameter of this
+        * command. At the moment this value is not used by the node but in the
+        * future this may be used to enforce certain node versions.
+        * 
+        * @return The expected version
+        */
+       public String getExpectedVersion() {
+               return expectedVersion;
+       }
+
+       /**
+        * Sets the value of the <code>ExpectedVersion</code> parameter of this
+        * command. At the moment this value is not used by the node but in the
+        * future this may be used to enforce certain node versions.
+        * 
+        * @param expectedVersion
+        *            The expected version
+        */
+       public void setExpectedVersion(String expectedVersion) {
+               this.expectedVersion = expectedVersion;
+       }
+
+       /**
+        * Returns the name of the client that is connecting.
+        * 
+        * @return The name of the client
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Sets the name of the client that is connecting.
+        * 
+        * @param name
+        *            The name of the client
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               writer.write("Name=" + name + LINEFEED);
+               writer.write("ExpectedVersion=" + expectedVersion + LINEFEED);
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPut.java b/src/de/todesbaum/util/freenet/fcp2/ClientPut.java
new file mode 100644 (file)
index 0000000..1d97ed3
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Abstract base class for all put requests. It contains all parameters that put
+ * requests have in common.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ClientPut.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public abstract class ClientPut extends Command {
+
+       /** The URI of this request. */
+       protected final String uri;
+       
+       /** The client token of this request. */
+       protected String clientToken = null;
+
+       /** Whether this request should only create a CHK. */
+       protected boolean getCHKOnly = false;
+
+       /** Whether this request is a global request. */
+       protected boolean global = false;
+
+       /** Whether the node should not try to compress the file. */
+       protected boolean dontCompress = false;
+
+       /** The maximum number of retries of this command. */
+       protected int maxRetries = 0;
+
+       /** The persistence of this request. */
+       protected Persistence persistence = Persistence.CONNECTION;
+
+       /** The priority class of this request. */
+       protected PriorityClass priorityClass = PriorityClass.INTERACTIVE;
+
+       /** The verbosiry of this request. */
+       protected Verbosity verbosity = Verbosity.NONE;
+
+       /**
+        * Creates a new put request with the specified name, identifier and URI.
+        * 
+        * @param name
+        *            The name of this request
+        * @param identifier
+        *            The identifier of this request
+        * @param uri
+        *            The URI of this request
+        */
+       protected ClientPut(String name, String identifier, String uri) {
+               super(name, identifier);
+               this.uri = uri;
+       }
+
+       /**
+        * Returns whether the node should not try to compress the data.
+        * 
+        * @return <code>true</code> if the node should <strong>not</strong> try
+        *         to compress the data
+        */
+       public boolean isDontCompress() {
+               return dontCompress;
+       }
+
+       /**
+        * Sets whether the node should not try to compress the data. A client might
+        * set this hint on data that is clearly not compressible, like MPEG audio
+        * files, JPEG or PNG images, highly compressed movies, or compressed
+        * archives like ZIP files. Otherwise the node will try to compress the file
+        * which -- depending on the size of the data -- might take a lot of time
+        * and memory.
+        * 
+        * @param dontCompress
+        *            <code>true</code> if the node should <strong>not</strong>
+        *            try to compress the data
+        */
+       public void setDontCompress(boolean dontCompress) {
+               this.dontCompress = dontCompress;
+       }
+
+       /**
+        * Returns whether this request should only return the CHK of the data.
+        * @return Whether this request should only return the CHK of the data
+        */
+       public boolean isGetCHKOnly() {
+               return getCHKOnly;
+       }
+
+       /**
+        * Sets whether this request should only return the CHK of the data.
+        * @param getCHKOnly
+        *            <code>true</code> if this request should only return the CHK of the data
+        */
+       public void setGetCHKOnly(boolean getCHKOnly) {
+               this.getCHKOnly = getCHKOnly;
+       }
+
+       /**
+        * Returns whether this request is a global request.
+        * @return <code>true</code> if this request is a global request, <code>false</code> otherwise
+        */
+       public boolean isGlobal() {
+               return global;
+       }
+
+       /**
+        * Sets whether this request is a global request.
+        * @param global
+        *            <code>true</code> if this request is a global request, <code>false</code> otherwise
+        */
+       public void setGlobal(boolean global) {
+               this.global = global;
+       }
+
+       /**
+        * Returns the maximum number of retries of this request.
+        * @return The maximum number of retries of this request
+        */
+       public int getMaxRetries() {
+               return maxRetries;
+       }
+
+       /**
+        * Sets the maximum number of retries of this request
+        * @param maxRetries
+        *            The maximum number of retries of this request
+        */
+       public void setMaxRetries(int maxRetries) {
+               this.maxRetries = maxRetries;
+       }
+
+       /**
+        * Returns the priority class of this request.
+        * @return The priority class of this request
+        */
+       public PriorityClass getPriorityClass() {
+               return priorityClass;
+       }
+
+       /**
+        * Sets the priority class of this request.
+        * @param priorityClass
+        *            The priority class of this request
+        */
+       public void setPriorityClass(PriorityClass priorityClass) {
+               this.priorityClass = priorityClass;
+       }
+
+       /**
+        * Returns the verbosity of this request.
+        * @return The verbosity of this request
+        */
+       public Verbosity getVerbosity() {
+               return verbosity;
+       }
+
+       /**
+        * Sets the verbosity of this request.
+        * @param verbosity
+        *            The verbosity of this request
+        */
+       public void setVerbosity(Verbosity verbosity) {
+               this.verbosity = verbosity;
+       }
+
+       /**
+        * Returns the URI of this request
+        * @return The URI of this request.
+        */
+       public String getUri() {
+               return uri;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               super.write(writer);
+               writer.write("URI=" + uri + LINEFEED);
+               if (verbosity != null)
+                       writer.write("Verbosity=" + verbosity.getValue() + LINEFEED);
+               if (maxRetries != 0)
+                       writer.write("MaxRetries=" + maxRetries + LINEFEED);
+               if (priorityClass != null)
+                       writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED);
+               writer.write("GetCHKOnly=" + getCHKOnly + LINEFEED);
+               writer.write("Global=" + global + LINEFEED);
+               writer.write("DontCompress=" + dontCompress + LINEFEED);
+               if (clientToken != null)
+                       writer.write("ClientToken=" + clientToken + LINEFEED);
+               if (persistence != null)
+                       writer.write("Persistence=" + persistence.getName() + LINEFEED);
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java
new file mode 100644 (file)
index 0000000..08c8a53
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Implementation of the <code>ClientPutComplexDir</code> command. This
+ * command can be used to insert directories that do not exist on disk.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ClientPutComplexDir.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public class ClientPutComplexDir extends ClientPutDir {
+
+       /** The file entries of this directory. */
+       private List<FileEntry> fileEntries = new ArrayList<FileEntry>();
+       
+       /** Whether this request has payload. */
+       private boolean hasPayload = false;
+       
+       /** The input streams for the payload. */
+       private List<InputStream> payloadInputStreams = new ArrayList<InputStream>();
+       
+       /** The total number of bytes of the payload. */
+       private long payloadLength = 0;
+
+       /**
+        * Creates a new <code>ClientPutComplexDir</code> command with the specified identifier and URI.
+        * @param identifier The identifier of the command
+        * @param uri The URI of the command
+        */
+       public ClientPutComplexDir(String identifier, String uri) {
+               super("ClientPutComplexDir", identifier, uri);
+       }
+
+       /**
+        * Adds a file to the directory inserted by this request.
+        * @param fileEntry The file entry to add to the directory
+        */
+       public void addFileEntry(FileEntry fileEntry) {
+               fileEntries.add(fileEntry);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               super.write(writer);
+               int fileIndex = 0;
+               for (FileEntry fileEntry: fileEntries) {
+                       writer.write("Files." + fileIndex + ".Name=" + fileEntry.getFilename() + LINEFEED);
+                       if (fileEntry.getContentType() != null) {
+                               writer.write("Files." + fileIndex + ".Metadata.ContentType=" + fileEntry.getContentType() + LINEFEED);
+                       }
+                       writer.write("Files." + fileIndex + ".UploadFrom=" + fileEntry.getName() + LINEFEED);
+                       if (fileEntry instanceof DirectFileEntry) {
+                               hasPayload = true;
+                               writer.write("Files." + fileIndex + ".DataLength=" + ((DirectFileEntry) fileEntry).getDataLength() + LINEFEED);
+                               payloadLength += ((DirectFileEntry) fileEntry).getDataLength();
+                               payloadInputStreams.add(((DirectFileEntry) fileEntry).getDataInputStream());
+                       } else if (fileEntry instanceof DiskFileEntry) {
+                               writer.write("Files." + fileIndex + ".Filename=" + ((DiskFileEntry) fileEntry).getFilename() + LINEFEED);
+                       } else if (fileEntry instanceof RedirectFileEntry) {
+                               writer.write("Files." + fileIndex + ".TargetURI=" + ((RedirectFileEntry) fileEntry).getTargetURI() + LINEFEED);
+                       }
+                       fileIndex++;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected boolean hasPayload() {
+               return hasPayload;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected long getPayloadLength() {
+               return payloadLength;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected InputStream getPayload() {
+               /* grr. use Vector here because it returns an Enumeration. */
+               Vector<InputStream> inputStreams = new Vector<InputStream>(payloadInputStreams);
+               return new SequenceInputStream(inputStreams.elements());
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java
new file mode 100644 (file)
index 0000000..1181e11
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Abstract base class for all put requests that insert a directory.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ClientPutDir.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public class ClientPutDir extends ClientPut {
+
+       /** The default file of the directory. */
+       protected String defaultName;
+
+       /**
+        * Creates a new request with the specified name, identifier, and URI.
+        * 
+        * @param name
+        *            The name of the request
+        * @param identifier
+        *            The identifier of the request
+        * @param uri
+        *            The URI of the request
+        */
+       public ClientPutDir(String name, String identifier, String uri) {
+               super(name, identifier, uri);
+       }
+
+       /**
+        * Returns the default name of the directory.
+        * 
+        * @return The default name of the directory
+        */
+       public String getDefaultName() {
+               return defaultName;
+       }
+
+       /**
+        * Sets the default name of the directory. The default name of a directory
+        * is the name of the file that will be delivered if the directory was
+        * requested without a filename. It's about the same as the
+        * <code>index.html</code> file that gets delivered if you only request a
+        * directory from a webserver.
+        * 
+        * @param defaultName
+        *            The default name of the directory
+        */
+       public void setDefaultName(String defaultName) {
+               this.defaultName = defaultName;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               super.write(writer);
+               if (defaultName != null)
+                       writer.write("DefaultName=" + defaultName + LINEFEED);
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Command.java b/src/de/todesbaum/util/freenet/fcp2/Command.java
new file mode 100644 (file)
index 0000000..5dd9593
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+
+/**
+ * Abstract base class for all commands.
+ * <p>
+ * In addition to the replies listed at the type comment of each specific
+ * command the node can <strong>always</strong> send the following messages:
+ * <code>ProtocolError</code> (if this library screws up),
+ * <code>CloseConnectionDuplicateClientName</code> (if a client with the same
+ * name of the {@link de.todesbaum.util.freenet.fcp2.Connection} connects). So
+ * when receiving messages from the node you should always be prepared for
+ * something you did not expect.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Command.java 371 2006-03-25 10:38:13Z bombe $
+ */
+public abstract class Command {
+
+       /** The line feed sequence used by the library. */
+       protected static final String LINEFEED = "\r\n";
+
+       /**
+        * The name of the command. The name is sent to the node so it can not be
+        * chosen arbitrarily!
+        */
+       private final String commandName;
+
+       /**
+        * The identifier of the command. This identifier is used to identify
+        * replies that are caused by a command.
+        */
+       private final String identifier;
+
+       /**
+        * Creates a new command with the specified name and identifier.
+        * 
+        * @param name
+        *            The name of the command
+        * @param identifier
+        *            The identifier of the command
+        */
+       public Command(String name, String identifier) {
+               this.commandName = name;
+               this.identifier = identifier;
+       }
+
+       /**
+        * Returns the name of this command.
+        * 
+        * @return The name of this command
+        */
+       public String getCommandName() {
+               return commandName;
+       }
+
+       /**
+        * Return the identifier of this command.
+        * 
+        * @return The identifier of this command
+        */
+       public String getIdentifier() {
+               return identifier;
+       }
+
+       /**
+        * Writes all parameters to the specified writer.
+        * <p>
+        * <strong>NOTE:</strong> Subclasses of Command <strong>must</strong> call
+        * <code>super.write(writer)</code> before or after writing their own
+        * parameters!
+        * 
+        * @param writer
+        *            The stream to write the parameters to
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       protected void write(Writer writer) throws IOException {
+               if (identifier != null)
+                       writer.write("Identifier=" + identifier + LINEFEED);
+       }
+
+       /**
+        * Returns whether this command has payload to send after the message.
+        * Subclasses need to return <code>true</code> here if they need to send
+        * payload after the message.
+        * 
+        * @return <code>true</code> if this command has payload to send,
+        *         <code>false</code> otherwise
+        */
+       protected boolean hasPayload() {
+               return false;
+       }
+
+       /**
+        * Returns the payload of this command as an {@link InputStream}. This
+        * method is never called if {@link #hasPayload()} returns
+        * <code>false</code>.
+        * 
+        * @return The payload of this command
+        */
+       protected InputStream getPayload() {
+               return null;
+       }
+
+       /**
+        * Returns the length of the payload. This method is never called if
+        * {@link #hasPayload()} returns <code>false</code>.
+        * 
+        * @return The length of the payload
+        */
+       protected long getPayloadLength() {
+               return -1;
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Connection.java b/src/de/todesbaum/util/freenet/fcp2/Connection.java
new file mode 100644 (file)
index 0000000..15757d0
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.todesbaum.util.io.LineInputStream;
+import de.todesbaum.util.io.StreamCopier;
+import de.todesbaum.util.io.TempFileInputStream;
+
+/**
+ * A physical connection to a Freenet node.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Connection.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public class Connection {
+
+       /** The listeners that receive events from this connection. */
+       private List<ConnectionListener> connectionListeners = new ArrayList<ConnectionListener>();
+
+       /** The node this connection is connected to. */
+       private final Node node;
+
+       /** The name of this connection. */
+       private final String name;
+
+       /** The network socket of this connection. */
+       private Socket nodeSocket;
+
+       /** The input stream that reads from the socket. */
+       private InputStream nodeInputStream;
+
+       /** The output stream that writes to the socket. */
+       private OutputStream nodeOutputStream;
+
+       /** The thread that reads from the socket. */
+       private NodeReader nodeReader;
+
+       /** A writer for the output stream. */
+       private Writer nodeWriter;
+
+       /** The NodeHello message sent by the node on connect. */
+       protected Message nodeHello;
+
+       /**
+        * Creates a new connection to the specified node with the specified name.
+        * 
+        * @param node
+        *            The node to connect to
+        * @param name
+        *            The name of this connection
+        */
+       public Connection(Node node, String name) {
+               this.node = node;
+               this.name = name;
+       }
+
+       /**
+        * Adds a listener that gets notified on connection events.
+        * 
+        * @param connectionListener
+        *            The listener to add
+        */
+       public void addConnectionListener(ConnectionListener connectionListener) {
+               connectionListeners.add(connectionListener);
+       }
+
+       /**
+        * Removes a listener from the list of registered listeners. Only the first
+        * matching listener is removed.
+        * 
+        * @param connectionListener
+        *            The listener to remove
+        * @see List#remove(java.lang.Object)
+        */
+       public void removeConnectionListener(ConnectionListener connectionListener) {
+               connectionListeners.remove(connectionListener);
+       }
+
+       /**
+        * Notifies listeners about a received message.
+        * 
+        * @param message
+        *            The received message
+        */
+       protected void fireMessageReceived(Message message) {
+               for (ConnectionListener connectionListener: connectionListeners) {
+                       connectionListener.messageReceived(this, message);
+               }
+       }
+
+       /**
+        * Notifies listeners about the loss of the connection.
+        */
+       protected void fireConnectionTerminated() {
+               for (ConnectionListener connectionListener: connectionListeners) {
+                       connectionListener.connectionTerminated(this);
+               }
+       }
+
+       /**
+        * Returns the name of the connection.
+        * 
+        * @return The name of the connection
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Connects to the node.
+        * 
+        * @return <code>true</code> if the connection succeeded and the node
+        *         returned a NodeHello message
+        * @throws IOException
+        *             if an I/O error occurs
+        * @see #getNodeHello()
+        */
+       public synchronized boolean connect() throws IOException {
+               nodeSocket = null;
+               nodeInputStream = null;
+               nodeOutputStream = null;
+               nodeWriter = null;
+               nodeReader = null;
+               try {
+                       nodeSocket = new Socket(node.getHostname(), node.getPort());
+                       nodeSocket.setReceiveBufferSize(65535);
+                       nodeInputStream = nodeSocket.getInputStream();
+                       nodeOutputStream = nodeSocket.getOutputStream();
+                       // nodeWriter = new TeeWriter(new
+                       // OutputStreamWriter(nodeOutputStream, Charset.forName("UTF-8")),
+                       // new PrintWriter(System.out));
+                       nodeWriter = new OutputStreamWriter(nodeOutputStream, Charset.forName("UTF-8"));
+                       nodeReader = new NodeReader(nodeInputStream);
+                       Thread nodeReaderThread = new Thread(nodeReader);
+                       nodeReaderThread.setDaemon(true);
+                       nodeReaderThread.start();
+                       ClientHello clientHello = new ClientHello();
+                       clientHello.setName(name);
+                       clientHello.setExpectedVersion("2.0");
+                       execute(clientHello);
+                       synchronized (this) {
+                               try {
+                                       wait();
+                               } catch (InterruptedException e) {
+                               }
+                       }
+                       return nodeHello != null;
+               } catch (IOException ioe1) {
+                       disconnect();
+                       throw ioe1;
+               }
+       }
+
+       /**
+        * Returns whether this connection is still connected to the node.
+        * 
+        * @return <code>true</code> if this connection is still valid,
+        *         <code>false</code> otherwise
+        */
+       public boolean isConnected() {
+               return (nodeHello != null) && (nodeSocket != null) && (nodeSocket.isConnected());
+       }
+
+       /**
+        * Returns the NodeHello message the node sent on connection.
+        * 
+        * @return The NodeHello message of the node
+        */
+       public Message getNodeHello() {
+               return nodeHello;
+       }
+
+       /**
+        * Disconnects from the node.
+        */
+       public void disconnect() {
+               if (nodeWriter != null) {
+                       try {
+                               nodeWriter.close();
+                       } catch (IOException ioe1) {
+                       }
+                       nodeWriter = null;
+               }
+               if (nodeOutputStream != null) {
+                       try {
+                               nodeOutputStream.close();
+                       } catch (IOException ioe1) {
+                       }
+                       nodeOutputStream = null;
+               }
+               if (nodeInputStream != null) {
+                       try {
+                               nodeInputStream.close();
+                       } catch (IOException ioe1) {
+                       }
+                       nodeInputStream = null;
+               }
+               if (nodeSocket != null) {
+                       try {
+                               nodeSocket.close();
+                       } catch (IOException ioe1) {
+                       }
+                       nodeSocket = null;
+               }
+               fireConnectionTerminated();
+       }
+
+       /**
+        * Executes the specified command.
+        * 
+        * @param command
+        *            The command to execute
+        * @throws IllegalStateException
+        *             if the connection is not connected
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public synchronized void execute(Command command) throws IllegalStateException, IOException {
+               if (nodeSocket == null) {
+                       throw new IllegalStateException("connection is not connected");
+               }
+               nodeWriter.write(command.getCommandName() + Command.LINEFEED);
+               command.write(nodeWriter);
+               nodeWriter.write("EndMessage" + Command.LINEFEED);
+               nodeWriter.flush();
+               if (command.hasPayload()) {
+                       StreamCopier.copy(command.getPayload(), nodeOutputStream, command.getPayloadLength());
+                       nodeOutputStream.flush();
+               }
+       }
+
+       /**
+        * The reader thread for this connection. This is essentially a thread that
+        * reads lines from the node, creates messages from them and notifies
+        * listeners about the messages.
+        * 
+        * @author David Roden &lt;droden@gmail.com&gt;
+        * @version $Id: Connection.java 413 2006-03-29 12:22:31Z bombe $
+        */
+       private class NodeReader implements Runnable {
+
+               /** The input stream to read from. */
+               @SuppressWarnings("hiding")
+               private InputStream nodeInputStream;
+
+               /**
+                * Creates a new reader that reads from the specified input stream.
+                * 
+                * @param nodeInputStream
+                *            The input stream to read from
+                */
+               public NodeReader(InputStream nodeInputStream) {
+                       this.nodeInputStream = nodeInputStream;
+               }
+
+               /**
+                * Main loop of the reader. Lines are read and converted into
+                * {@link Message} objects.
+                */
+               public void run() {
+                       LineInputStream nodeReader = null;
+                       try {
+                               nodeReader = new LineInputStream(nodeInputStream);
+                               String line = "";
+                               Message message = null;
+                               while (line != null) {
+                                       line = nodeReader.readLine();
+                                       // System.err.println("> " + line);
+                                       if (line == null) {
+                                               break;
+                                       }
+                                       if (message == null) {
+                                               message = new Message(line);
+                                               continue;
+                                       }
+                                       if ("Data".equals(line)) {
+                                               /* need to read message from stream now */
+                                               File tempFile = null;
+                                               try {
+                                                       tempFile = File.createTempFile("fcpv2", "data");
+                                                       tempFile.deleteOnExit();
+                                                       FileOutputStream tempFileOutputStream = new FileOutputStream(tempFile);
+                                                       long dataLength = Long.parseLong(message.get("DataLength"));
+                                                       StreamCopier.copy(nodeInputStream, tempFileOutputStream, dataLength);
+                                                       tempFileOutputStream.close();
+                                                       message.setPayloadInputStream(new TempFileInputStream(tempFile));
+                                               } catch (IOException ioe1) {
+                                                       ioe1.printStackTrace();
+                                               }
+                                       }
+                                       if ("Data".equals(line) || "EndMessage".equals(line)) {
+                                               if (message.getName().equals("NodeHello")) {
+                                                       nodeHello = message;
+                                                       synchronized (Connection.this) {
+                                                               Connection.this.notify();
+                                                       }
+                                               } else {
+                                                       fireMessageReceived(message);
+                                               }
+                                               message = null;
+                                               continue;
+                                       }
+                                       int equalsPosition = line.indexOf('=');
+                                       if (equalsPosition > -1) {
+                                               String key = line.substring(0, equalsPosition).trim();
+                                               String value = line.substring(equalsPosition + 1).trim();
+                                               if (key.equals("Identifier")) {
+                                                       message.setIdentifier(value);
+                                               } else {
+                                                       message.put(key, value);
+                                               }
+                                               continue;
+                                       }
+                                       /* skip lines consisting of whitespace only */
+                                       if (line.trim().length() == 0) {
+                                               continue;
+                                       }
+                                       /* if we got here, some error occured! */
+                                       throw new IOException("Unexpected line: " + line);
+                               }
+                       } catch (IOException ioe1) {
+                               // ioe1.printStackTrace();
+                       } finally {
+                               if (nodeReader != null) {
+                                       try {
+                                               nodeReader.close();
+                                       } catch (IOException ioe1) {
+                                       }
+                               }
+                               if (nodeInputStream != null) {
+                                       try {
+                                               nodeInputStream.close();
+                                       } catch (IOException ioe1) {
+                                       }
+                               }
+                       }
+                       Connection.this.disconnect();
+               }
+
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java b/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java
new file mode 100644 (file)
index 0000000..0bc77bb
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.util.EventListener;
+
+/**
+ * Interface for clients that want to be notified when a message was received.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ConnectionListener.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public interface ConnectionListener extends EventListener {
+
+       /**
+        * Notifies a client that a message was received.
+        * 
+        * @param connection
+        *            The connection the message was received on
+        * @param message
+        *            The message that was received
+        */
+       public void messageReceived(Connection connection, Message message);
+
+       /**
+        * Notifies a client that the connection to the node has been lost.
+        * 
+        * @param connection
+        *            The connection that was lost
+        */
+       public void connectionTerminated(Connection connection);
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java
new file mode 100644 (file)
index 0000000..a337d17
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * A {@link FileEntry} that sends its payload directly to the node, using the
+ * existing FCP connection.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: DirectFileEntry.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public class DirectFileEntry extends FileEntry {
+
+       /** The input stream to read the data for this file from. */
+       private final InputStream dataInputStream;
+
+       /** The length of the data. */
+       private final long dataLength;
+
+       /**
+        * Creates a new FileEntry with the specified name and content type that
+        * gets its data from the specified byte array.
+        * 
+        * @param filename
+        *            The name of the file
+        * @param contentType
+        *            The content type of the file
+        * @param dataBytes
+        *            The content of the file
+        */
+       public DirectFileEntry(String filename, String contentType, byte[] dataBytes) {
+               this(filename, contentType, new ByteArrayInputStream(dataBytes), dataBytes.length);
+       }
+
+       /**
+        * Creates a new FileEntry with the specified name and content type that
+        * gets its data from the specified input stream.
+        * 
+        * @param filename
+        *            The name of the file
+        * @param contentType
+        *            The content type of the file
+        * @param dataInputStream
+        *            The input stream to read the content from
+        * @param dataLength
+        *            The length of the data input stream
+        */
+       public DirectFileEntry(String filename, String contentType, InputStream dataInputStream, long dataLength) {
+               super(filename, contentType);
+               this.dataInputStream = dataInputStream;
+               this.dataLength = dataLength;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getName() {
+               return "direct";
+       }
+
+       /**
+        * Returns the input stream for the file's content.
+        * 
+        * @return The input stream for the file's content
+        */
+       public InputStream getDataInputStream() {
+               return dataInputStream;
+       }
+
+       /**
+        * Returns the length of this file's content.
+        * 
+        * @return The length of this file's content
+        */
+       public long getDataLength() {
+               return dataLength;
+       }
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java
new file mode 100644 (file)
index 0000000..c4fed77
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * A {@link FileEntry} that reads the content from a file on the disk.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: DiskFileEntry.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public class DiskFileEntry extends FileEntry {
+
+       /** The local file name. */
+       private final String localFilename;
+
+       /**
+        * Creates a new {@link FileEntry} with the specified name and content type
+        * that is read from the file specified by <code>localFilename</code>.
+        * 
+        * @param filename
+        *            The name of the file
+        * @param contentType
+        *            The content type of the file
+        * @param localFilename
+        *            The name of the local file that holds the content of the file
+        *            to insert
+        */
+       public DiskFileEntry(String filename, String contentType, String localFilename) {
+               super(filename, contentType);
+               this.localFilename = localFilename;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getName() {
+               return "disk";
+       }
+
+       /**
+        * Returns the name of the local file that holds the content for this file.
+        * 
+        * @return The name of the local file
+        */
+       public String getLocalFilename() {
+               return localFilename;
+       }
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/util/freenet/fcp2/FileEntry.java b/src/de/todesbaum/util/freenet/fcp2/FileEntry.java
new file mode 100644 (file)
index 0000000..7567725
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * Abstract base class of file entries that are used in the
+ * {@link de.todesbaum.util.freenet.fcp2.ClientPutComplexDir} command to define
+ * the files of an insert.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: FileEntry.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public abstract class FileEntry {
+
+       /** The name of the file. */
+       private final String filename;
+
+       /** The content type of the file. */
+       private final String contentType;
+
+       /**
+        * Creates a new file entry with the specified name and content type. The
+        * content type should be a standard MIME type with an additional charset
+        * specification for text-based types.
+        * 
+        * @param filename
+        *            The name of the file
+        * @param contentType
+        *            The content type of the file, e.g.
+        *            <code>"application/x-tar"</code> or
+        *            <code>"text/html; charset=iso8859-15"</code>
+        */
+       protected FileEntry(String filename, String contentType) {
+               this.filename = filename;
+               this.contentType = contentType;
+       }
+
+       /**
+        * Returns the name of this entry's type. Can be one of <code>direct</code>,
+        * <code>disk</code>, or <code>redirect</code>. This method is
+        * implemented by the subclasses {@link DirectFileEntry},
+        * {@link DiskFileEntry}, and {@link RedirectFileEntry}, respectively.
+        * 
+        * @return The name of this entry's type
+        */
+       public abstract String getName();
+
+       /**
+        * Returns the content type of this file.
+        * 
+        * @return The content type of this file
+        */
+       public String getContentType() {
+               return contentType;
+       }
+
+       /**
+        * Returns the name of this file.
+        * 
+        * @return The name of this file
+        */
+       public String getFilename() {
+               return filename;
+       }
+
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java b/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java
new file mode 100644 (file)
index 0000000..cfeaa74
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * Implementation of the <code>GenerateSSK</code> command.
+ * <p>
+ * The node can answer with the following messages: <code>SSKKeypair</code>.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: GenerateSSK.java 413 2006-03-29 12:22:31Z bombe $
+ */
+public class GenerateSSK extends Command {
+
+       /**
+        * Creates a new <code>GenerateSSK</code> request.
+        */
+       public GenerateSSK() {
+               super("GenerateSSK", null);
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Message.java b/src/de/todesbaum/util/freenet/fcp2/Message.java
new file mode 100644 (file)
index 0000000..a8460f8
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+/**
+ * Contains replies sent by the Freenet node. A message always has a name, and
+ * most of the messages also have an identifier which binds it to a specific
+ * command. Exceptions are among others <code>NodeHello</code>,
+ * <code>SSKKeypair</code>, and <code>EndListPersistentRequests</code>.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Message.java 413 2006-03-29 12:22:31Z bombe $
+ * @see de.todesbaum.util.freenet.fcp2.Client
+ */
+public class Message {
+
+       /** The name of this message. */
+       private final String name;
+
+       /** The identifier of this message. */
+       private String identifier = "";
+
+       /** The parameters of this message. */
+       private Map<String, String> parameters = new HashMap<String, String>();
+
+       /** The payload. */
+       private InputStream payloadInputStream;
+
+       /**
+        * Creates a new message with the specified name.
+        * 
+        * @param name
+        *            The name of this message
+        */
+       public Message(String name) {
+               this.name = name;
+       }
+
+       /**
+        * Returns the identifier of this message.
+        * 
+        * @return The identifier
+        */
+       public String getIdentifier() {
+               return identifier;
+       }
+
+       /**
+        * Sets the identifier of this message.
+        * 
+        * @param identifier
+        *            The identifier of this message
+        */
+       public void setIdentifier(String identifier) {
+               this.identifier = identifier;
+       }
+
+       /**
+        * Returns the name of this message.
+        * 
+        * @return The name of this message
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Tests whether this message contains the parameter with the specified key.
+        * Key names are compared ignoring case.
+        * 
+        * @param key
+        *            The name of the parameter
+        * @return <code>true</code> if this parameter exists in this message,
+        *         <code>false</code> otherwise
+        */
+       public boolean containsKey(String key) {
+               return parameters.containsKey(key.toLowerCase());
+       }
+
+       /**
+        * Returns all parameters of this message. The keys of the entries are all
+        * lower case so if you want to match the parameter names you have to watch
+        * out.
+        * 
+        * @return All parameters of this message
+        */
+       public Set<Entry<String, String>> entrySet() {
+               return parameters.entrySet();
+       }
+
+       /**
+        * Returns the value of the parameter with the name specified by
+        * <code>key</code>.
+        * 
+        * @param key
+        *            The name of the parameter
+        * @return The value of the parameter
+        */
+       public String get(String key) {
+               return parameters.get(key.toLowerCase());
+       }
+
+       /**
+        * Stores the specified value as parameter with the name specified by
+        * <code>key</code>.
+        * 
+        * @param key
+        *            The name of the parameter
+        * @param value
+        *            The value of the parameter
+        * @return The previous value, or <code>null</code> if there was no
+        *         previous value
+        */
+       public String put(String key, String value) {
+               return parameters.put(key.toLowerCase(), value);
+       }
+
+       /**
+        * Returns the number of parameters in this message.
+        * 
+        * @return The number of parameters
+        */
+       public int size() {
+               return parameters.size();
+       }
+
+       /**
+        * @return Returns the payloadInputStream.
+        */
+       public InputStream getPayloadInputStream() {
+               return payloadInputStream;
+       }
+
+       /**
+        * @param payloadInputStream
+        *            The payloadInputStream to set.
+        */
+       public void setPayloadInputStream(InputStream payloadInputStream) {
+               this.payloadInputStream = payloadInputStream;
+       }
+
+       /**
+        * Returns a textual representation of this message, containing its name,
+        * the identifier, and the parameters.
+        * 
+        * @return A textual representation of this message
+        */
+       public String toString() {
+               return name + "[identifier=" + identifier + ",parameters=" + parameters.toString() + "]";
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Node.java b/src/de/todesbaum/util/freenet/fcp2/Node.java
new file mode 100644 (file)
index 0000000..e444d22
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * Contains the hostname and port number of the Freenet node.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Node.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public class Node {
+
+       /** The default port of FCPv2. */
+       public static final int DEFAULT_PORT = 9481;
+
+       /** The hostname of the node. */
+       protected String hostname;
+
+       /** The port number of the node. */
+       protected int port;
+
+       /**
+        * Creates a new node with the specified hostname and the default port
+        * number.
+        * 
+        * @param hostname
+        *            The hostname of the node
+        * @see #DEFAULT_PORT
+        */
+       public Node(String hostname) {
+               this(hostname, DEFAULT_PORT);
+       }
+
+       /**
+        * Creates a new node with the specified hostname and port number.
+        * 
+        * @param hostname
+        *            The hostname of the node
+        * @param port
+        *            The port number of the node
+        */
+       public Node(String hostname, int port) {
+               this.hostname = hostname;
+               this.port = port;
+       }
+
+       /**
+        * Returns the hostname of the node.
+        * 
+        * @return The hostname of the node
+        */
+       public String getHostname() {
+               return hostname;
+       }
+
+       /**
+        * Returns the port number of the node.
+        * 
+        * @return The port number of the node
+        */
+       public int getPort() {
+               return port;
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Persistence.java b/src/de/todesbaum/util/freenet/fcp2/Persistence.java
new file mode 100644 (file)
index 0000000..105fdb1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Persistence.java 373 2006-03-25 10:42:52Z bombe $
+ */
+public final class Persistence {
+
+       public static final Persistence CONNECTION = new Persistence("connection");
+       public static final Persistence REBOOT = new Persistence("reboot");
+       public static final Persistence FOREVER = new Persistence("forever");
+
+       private String name;
+
+       private Persistence(String name) {
+               this.name = name;
+       }
+
+       /**
+        * @return Returns the name.
+        */
+       public String getName() {
+               return name;
+       }
+
+       public String toString() {
+               return name;
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java b/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java
new file mode 100644 (file)
index 0000000..9f9ea0e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: PriorityClass.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public final class PriorityClass {
+
+       public static final PriorityClass MAXIMUM = new PriorityClass("maximum", 0);
+       public static final PriorityClass INTERACTIVE = new PriorityClass("interactive", 1);
+       public static final PriorityClass SEMI_INTERACTIVE = new PriorityClass("semiInteractive", 2);
+       public static final PriorityClass UPDATABLE = new PriorityClass("updatable", 3);
+       public static final PriorityClass BULK = new PriorityClass("bulk", 4);
+       public static final PriorityClass PREFETCH = new PriorityClass("prefetch", 5);
+       public static final PriorityClass MINIMUM = new PriorityClass("minimum", 6);
+
+       private String name;
+       private int value;
+
+       private PriorityClass(String name, int value) {
+               this.name = name;
+               this.value = value;
+       }
+
+       /**
+        * @return Returns the name.
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * @return Returns the value.
+        */
+       public int getValue() {
+               return value;
+       }
+
+}
diff --git a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java
new file mode 100644 (file)
index 0000000..cecb5a4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+public class RedirectFileEntry extends FileEntry {
+
+       final String targetURI;
+
+       public RedirectFileEntry(String filename, String contentType, String targetURI) {
+               super(filename, contentType);
+               this.targetURI = targetURI;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getName() {
+               return "redirect";
+       }
+
+       /**
+        * @return Returns the targetURI.
+        */
+       public String getTargetURI() {
+               return targetURI;
+       }
+}
\ No newline at end of file
diff --git a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/de/todesbaum/util/freenet/fcp2/Verbosity.java
new file mode 100644 (file)
index 0000000..f027779
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.freenet.fcp2;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: Verbosity.java 356 2006-03-24 15:13:38Z bombe $
+ */
+public final class Verbosity {
+
+       public static final Verbosity PROGRESS = new Verbosity(1);
+       public static final Verbosity COMPRESSION = new Verbosity(512);
+
+       public static final Verbosity NONE = new Verbosity(0);
+       public static final Verbosity ALL = new Verbosity(PROGRESS, COMPRESSION);
+
+       private final int value;
+
+       private Verbosity(int value) {
+               this.value = value;
+       }
+
+       private Verbosity(Verbosity verbosity1, Verbosity verbosity2) {
+               this(verbosity1.value | verbosity2.value);
+       }
+
+       /**
+        * @return Returns the value.
+        */
+       public int getValue() {
+               return value;
+       }
+
+}
diff --git a/src/de/todesbaum/util/image/IconLoader.java b/src/de/todesbaum/util/image/IconLoader.java
new file mode 100644 (file)
index 0000000..a889481
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.image;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+
+/**
+ * @author <a href="mailto:droden@gmail.com">David Roden</a>
+ * @version $Id: IconLoader.java 130 2006-02-18 18:27:11Z bombe $
+ */
+public class IconLoader {
+
+       public static Icon loadIcon(String resourceName) {
+               try {
+                       InputStream resourceStream = IconLoader.class.getResourceAsStream(resourceName);
+                       if (resourceStream == null) {
+                               return null;
+                       }
+                       ByteArrayOutputStream imageOutput = new ByteArrayOutputStream();
+                       byte[] buffer = new byte[16384];
+                       int r = 0;
+                       while ((r = resourceStream.read(buffer)) != -1) {
+                               imageOutput.write(buffer, 0, r);
+                       }
+                       imageOutput.flush();
+                       return new ImageIcon(imageOutput.toByteArray());
+               } catch (IOException e) {
+               }
+               return null;
+       }
+
+}
diff --git a/src/de/todesbaum/util/io/LineInputStream.java b/src/de/todesbaum/util/io/LineInputStream.java
new file mode 100644 (file)
index 0000000..48500bb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: LineInputStream.java 429 2006-03-29 18:04:48Z bombe $
+ */
+public class LineInputStream extends FilterInputStream {
+       
+       private boolean skipLinefeed = false;
+       private StringBuffer lineBuffer = new StringBuffer();
+
+       /**
+        * @param in
+        */
+       public LineInputStream(InputStream in) {
+               super(in);
+       }
+
+       public synchronized String readLine() throws IOException {
+               lineBuffer.setLength(0);
+               int c = 0;
+               while (c != -1) {
+                       c = read();
+                       if ((c == -1) && lineBuffer.length() == 0)
+                               return null;
+                       if (skipLinefeed && (c == '\n')) {
+                               skipLinefeed = false;
+                               continue;
+                       }
+                       skipLinefeed = (c == '\r');
+                       if ((c == '\r') || (c == '\n')) {
+                               c = -1;
+                       } else {
+                               lineBuffer.append((char) c);
+                       }
+               }
+               return lineBuffer.toString();
+       }
+       
+}
diff --git a/src/de/todesbaum/util/io/ReplacingOutputStream.java b/src/de/todesbaum/util/io/ReplacingOutputStream.java
new file mode 100644 (file)
index 0000000..65e9c5d
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: ReplacingOutputStream.java 431 2006-03-29 18:06:18Z bombe $
+ */
+public class ReplacingOutputStream extends FilterOutputStream {
+
+       private Map<String, String> replacements = new HashMap<String, String>();
+       private StringBuffer ringBuffer = new StringBuffer();
+       
+       /**
+        * @param out
+        */
+       public ReplacingOutputStream(OutputStream out) {
+               super(out);
+       }
+
+       public void addReplacement(String token, String value) {
+               replacements.put(token, value);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void write(int b) throws IOException {
+               ringBuffer.append((char) b);
+               Iterator<Entry<String, String>> entries = replacements.entrySet().iterator();
+               boolean found = false;
+               Entry<String, String> entry = null;
+               while (!found && entries.hasNext()) {
+                       entry = entries.next();
+                       if (entry.getKey().startsWith(ringBuffer.toString())) {
+                               found = true;
+                       }
+               }
+               if (!found) {
+                       String buffer = ringBuffer.toString();
+                       for (int index = 0, size = buffer.length(); index < size; index++) {
+                               super.write(buffer.charAt(index));
+                       }
+                       ringBuffer.setLength(0);
+               } else {
+                       if (entry.getKey().equals(ringBuffer.toString())) {
+                               String buffer = entry.getValue();
+                               for (int index = 0, size = buffer.length(); index < size; index++) {
+                                       super.write(buffer.charAt(index));
+                               }
+                               ringBuffer.setLength(0);
+                       }
+               }
+       }
+       
+}
diff --git a/src/de/todesbaum/util/io/StreamCopier.java b/src/de/todesbaum/util/io/StreamCopier.java
new file mode 100644 (file)
index 0000000..982d556
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.io;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies input from an {@link InputStream} to an {@link OutputStream}.
+ * 
+ * @author <a href="mailto:droden@gmail.com">David Roden</a>
+ * @version $Id: StreamCopier.java 428 2006-03-29 18:03:36Z bombe $
+ */
+public class StreamCopier {
+
+       /**
+        * The default size of the buffer.
+        */
+       private static final int BUFFER_SIZE = 64 * 1024;
+
+       /**
+        * The {@link InputStream} to read from.
+        */
+       private InputStream inputStream;
+
+       /**
+        * The {@link OutputStream} to write to.
+        */
+       private OutputStream outputStream;
+
+       /**
+        * The number of bytes to copy.
+        */
+       private long length;
+
+       /**
+        * The size of the buffer.
+        */
+       private int bufferSize;
+
+       /**
+        * Creates a new StreamCopier with the specified parameters and the default
+        * buffer size.
+        * 
+        * @param inputStream
+        *            The {@link InputStream} to read from
+        * @param outputStream
+        *            The {@link OutputStream} to write to
+        * @param length
+        *            The number of bytes to copy
+        */
+       public StreamCopier(InputStream inputStream, OutputStream outputStream, long length) {
+               this(inputStream, outputStream, length, BUFFER_SIZE);
+       }
+
+       /**
+        * Creates a new StreamCopier with the specified parameters and the default
+        * buffer size.
+        * 
+        * @param inputStream
+        *            The {@link InputStream} to read from
+        * @param outputStream
+        *            The {@link OutputStream} to write to
+        * @param length
+        *            The number of bytes to copy
+        * @param bufferSize
+        *            The number of bytes to copy at a time
+        */
+       public StreamCopier(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) {
+               this.inputStream = inputStream;
+               this.outputStream = outputStream;
+               this.length = length;
+               this.bufferSize = bufferSize;
+       }
+
+       /**
+        * Copies the stream data. If the input stream is depleted before the
+        * requested number of bytes have been read an {@link EOFException} is
+        * thrown.
+        * 
+        * @throws EOFException
+        *             if the input stream is depleted before the requested number
+        *             of bytes has been read
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void copy() throws EOFException, IOException {
+               copy(inputStream, outputStream, length, bufferSize);
+       }
+
+       /**
+        * Copies <code>length</code> bytes from the <code>inputStream</code> to
+        * the <code>outputStream</code>.
+        * 
+        * @param inputStream
+        *            The input stream to read from
+        * @param outputStream
+        *            The output stream to write to
+        * @param length
+        *            The number of bytes to copy
+        * @throws IOException
+        *             if an I/O exception occurs
+        */
+       public static void copy(InputStream inputStream, OutputStream outputStream, long length) throws IOException {
+               copy(inputStream, outputStream, length, BUFFER_SIZE);
+       }
+
+       /**
+        * Copies <code>length</code> bytes from the <code>inputStream</code> to
+        * the <code>outputStream</code> using a buffer with the specified size
+        * 
+        * @param inputStream
+        *            The input stream to read from
+        * @param outputStream
+        *            The output stream to write to
+        * @param length
+        *            The number of bytes to copy
+        * @param bufferSize
+        *            The size of the copy buffer
+        * @throws IOException
+        *             if an I/O exception occurs
+        */
+       public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) throws IOException {
+               long remaining = length;
+               byte[] buffer = new byte[bufferSize];
+               while (remaining > 0) {
+                       int read = inputStream.read(buffer, 0, (int) Math.min(Integer.MAX_VALUE, Math.min(bufferSize, remaining)));
+                       if (read == -1) {
+                               throw new EOFException();
+                       }
+                       outputStream.write(buffer, 0, read);
+                       remaining -= read;
+               }
+       }
+
+}
diff --git a/src/de/todesbaum/util/io/TempFileInputStream.java b/src/de/todesbaum/util/io/TempFileInputStream.java
new file mode 100644 (file)
index 0000000..0cefd79
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id$
+ */
+public class TempFileInputStream extends FileInputStream {
+
+       private File tempFile;
+
+       /**
+        * @param name
+        * @throws FileNotFoundException
+        */
+       public TempFileInputStream(String name) throws FileNotFoundException {
+               this(new File(name));
+       }
+
+       /**
+        * @param file
+        * @throws FileNotFoundException
+        */
+       public TempFileInputStream(File file) throws FileNotFoundException {
+               super(file);
+       }
+
+       public void close() throws IOException {
+               super.close();
+               tempFile.delete();
+       }
+
+}
diff --git a/src/de/todesbaum/util/mime/DefaultMIMETypes.java b/src/de/todesbaum/util/mime/DefaultMIMETypes.java
new file mode 100644 (file)
index 0000000..c821912
--- /dev/null
@@ -0,0 +1,834 @@
+/* taken from freenet (http://www.freenetproject.org/) */ 
+package de.todesbaum.util.mime;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+/**
+ * Holds the default MIME types.
+ */
+public class DefaultMIMETypes {
+       
+       /** Default MIME type - what to set it to if we don't know any better */
+       public static final String DEFAULT_MIME_TYPE = "application/octet-stream";
+       
+       /** MIME types: number -> name */
+       private static List<String> mimeTypesByNumber = new Vector<String>();
+       
+       /** MIME types: name -> number */
+       private static Map<String, Short> mimeTypesByName = new HashMap<String, Short>();
+       
+       /** MIME types by extension. One extension maps to one MIME type, but not necessarily
+        * the other way around. */
+       private static Map<String, Short> mimeTypesByExtension = new HashMap<String, Short>();
+       
+       /** Primary extension by MIME type number. */
+       private static Map<Short, String> primaryExtensionByMimeNumber = new HashMap<Short, String>();
+       
+       /**
+        * Add a MIME type, without any extensions.
+        * @param number The number of the MIME type for compression. This *must not change*
+        * for a given type, or the metadata format will be affected.
+        * @param type The actual MIME type string. Do not include ;charset= etc; these are
+        * parameters and there is a separate mechanism for them.
+        */
+       protected static synchronized void addMIMEType(short number, String type) {
+               if(mimeTypesByNumber.size() > number) {
+                       String s = mimeTypesByNumber.get(number);
+                       if(s != null) throw new IllegalArgumentException("Already used: "+number);
+               } else {
+                       mimeTypesByNumber.add(number, null);
+               }
+               mimeTypesByNumber.set(number, type);
+               mimeTypesByName.put(type, new Short(number));
+       }
+
+       /**
+        * Add a MIME type.
+        * @param number The number of the MIME type for compression. This *must not change*
+        * for a given type, or the metadata format will be affected.
+        * @param type The actual MIME type string. Do not include ;charset= etc; these are
+        * parameters and there is a separate mechanism for them.
+        * @param extensions An array of common extensions for files of this type. Must be
+        * unique for the type.
+        */
+       protected static synchronized void addMIMEType(short number, String type, String[] extensions, String outExtension) {
+               addMIMEType(number, type);
+               Short t = new Short(number);
+               if(extensions != null) {
+                       for(int i=0;i<extensions.length;i++) {
+                               String ext = extensions[i].toLowerCase();
+                               if(mimeTypesByExtension.containsKey(ext)) {
+                                       // No big deal
+                                       //Short s = mimeTypesByExtension.get(ext);
+                               } else {
+                                       // If only one, make it primary
+                                       if(outExtension == null && extensions.length == 1)
+                                               primaryExtensionByMimeNumber.put(t, ext);
+                                       mimeTypesByExtension.put(ext, t);
+                               }
+                       }
+               }
+               if(outExtension != null)
+                       primaryExtensionByMimeNumber.put(t, outExtension);
+                               
+       }
+
+       /**
+        * Add a MIME type, with extensions separated by spaces. This is more or less
+        * the format in /etc/mime-types.
+        */
+       protected static synchronized void addMIMEType(short number, String type, String extensions) {
+               addMIMEType(number, type, extensions.split(" "), null);
+       }
+
+       /**
+        * Add a MIME type, with extensions separated by spaces. This is more or less
+        * the format in /etc/mime-types.
+        */
+       protected static synchronized void addMIMEType(short number, String type, String extensions, String outExtension) {
+               addMIMEType(number, type, extensions.split(" "), outExtension);
+       }
+       
+       /**
+        * Get a known MIME type by number.
+        */
+       public static String byNumber(short x) {
+               if(x > mimeTypesByNumber.size() || x < 0)
+                       return null;
+               return mimeTypesByNumber.get(x);
+       }
+       
+       /**
+        * Get the number of a MIME type, or -1 if it is not in the table of known MIME
+        * types, in which case it will have to be sent uncompressed.
+        */
+       public static short byName(String s) {
+               Short x = mimeTypesByName.get(s);
+               if(x != null) return x.shortValue();
+               return -1;
+       }
+       
+       /* From toad's /etc/mime.types
+        * cat /etc/mime.types | sed "/^$/d;/#/d" | tr --squeeze '\t' ' ' | 
+        * (y=0; while read x; do echo "$x" | 
+        * sed -n "s/^\([^ ]*\)$/addMIMEType\($y, \"\1\"\);/p;s/^\([^ (),]\+\) \(.*\)$/addMIMEType\($y, \"\1\", \"\2\"\);/p;"; y=$((y+1)); done)
+        */
+       
+       static {
+               addMIMEType((short) 0, "application/activemessage");
+               addMIMEType((short) 1, "application/andrew-inset", "ez");
+               addMIMEType((short) 2, "application/applefile");
+               addMIMEType((short) 3, "application/atomicmail");
+               addMIMEType((short) 4, "application/batch-SMTP");
+               addMIMEType((short) 5, "application/beep+xml");
+               addMIMEType((short) 6, "application/cals-1840");
+               addMIMEType((short) 7, "application/commonground");
+               addMIMEType((short) 8, "application/cu-seeme", "cu");
+               addMIMEType((short) 9, "application/cybercash");
+               addMIMEType((short) 10, "application/dca-rft");
+               addMIMEType((short) 11, "application/dec-dx");
+               addMIMEType((short) 12, "application/docbook+xml");
+               addMIMEType((short) 13, "application/dsptype", "tsp");
+               addMIMEType((short) 14, "application/dvcs");
+               addMIMEType((short) 15, "application/edi-consent");
+               addMIMEType((short) 16, "application/edi-x12");
+               addMIMEType((short) 17, "application/edifact");
+               addMIMEType((short) 18, "application/eshop");
+               addMIMEType((short) 19, "application/font-tdpfr");
+               addMIMEType((short) 20, "application/futuresplash", "spl");
+               addMIMEType((short) 21, "application/ghostview");
+               addMIMEType((short) 22, "application/hta", "hta");
+               addMIMEType((short) 23, "application/http");
+               addMIMEType((short) 24, "application/hyperstudio");
+               addMIMEType((short) 25, "application/iges");
+               addMIMEType((short) 26, "application/index");
+               addMIMEType((short) 27, "application/index.cmd");
+               addMIMEType((short) 28, "application/index.obj");
+               addMIMEType((short) 29, "application/index.response");
+               addMIMEType((short) 30, "application/index.vnd");
+               addMIMEType((short) 31, "application/iotp");
+               addMIMEType((short) 32, "application/ipp");
+               addMIMEType((short) 33, "application/isup");
+               addMIMEType((short) 34, "application/java-archive", "jar");
+               addMIMEType((short) 35, "application/java-serialized-object", "ser");
+               addMIMEType((short) 36, "application/java-vm", "class");
+               addMIMEType((short) 37, "application/mac-binhex40", "hqx");
+               addMIMEType((short) 38, "application/mac-compactpro", "cpt");
+               addMIMEType((short) 39, "application/macwriteii");
+               addMIMEType((short) 40, "application/marc");
+               addMIMEType((short) 41, "application/mathematica", "nb");
+               addMIMEType((short) 42, "application/mathematica-old");
+               addMIMEType((short) 43, "application/msaccess", "mdb");
+               addMIMEType((short) 44, "application/msword", "doc dot");
+               addMIMEType((short) 45, "application/news-message-id");
+               addMIMEType((short) 46, "application/news-transmission");
+               addMIMEType((short) 47, "application/ocsp-request");
+               addMIMEType((short) 48, "application/ocsp-response");
+               addMIMEType((short) 49, "application/octet-stream", "bin");
+               addMIMEType((short) 50, "application/oda", "oda");
+               addMIMEType((short) 51, "application/ogg", "ogg");
+               addMIMEType((short) 52, "application/parityfec");
+               addMIMEType((short) 53, "application/pdf", "pdf");
+               addMIMEType((short) 54, "application/pgp-encrypted");
+               addMIMEType((short) 55, "application/pgp-keys", "key");
+               addMIMEType((short) 56, "application/pgp-signature", "pgp");
+               addMIMEType((short) 57, "application/pics-rules", "prf");
+               addMIMEType((short) 58, "application/pkcs10");
+               addMIMEType((short) 59, "application/pkcs7-mime");
+               addMIMEType((short) 60, "application/pkcs7-signature");
+               addMIMEType((short) 61, "application/pkix-cert");
+               addMIMEType((short) 62, "application/pkix-crl");
+               addMIMEType((short) 63, "application/pkixcmp");
+               addMIMEType((short) 64, "application/postscript", "ps ai eps");
+               addMIMEType((short) 65, "application/prs.alvestrand.titrax-sheet");
+               addMIMEType((short) 66, "application/prs.cww");
+               addMIMEType((short) 67, "application/prs.nprend");
+               addMIMEType((short) 68, "application/qsig");
+               addMIMEType((short) 69, "application/rar", "rar");
+               addMIMEType((short) 70, "application/rdf+xml", "rdf");
+               addMIMEType((short) 71, "application/remote-printing");
+               addMIMEType((short) 72, "application/riscos");
+               addMIMEType((short) 73, "application/rss+xml", "rss");
+               addMIMEType((short) 74, "application/rtf");
+               addMIMEType((short) 75, "application/sdp");
+               addMIMEType((short) 76, "application/set-payment");
+               addMIMEType((short) 77, "application/set-payment-initiation");
+               addMIMEType((short) 78, "application/set-registration");
+               addMIMEType((short) 79, "application/set-registration-initiation");
+               addMIMEType((short) 80, "application/sgml");
+               addMIMEType((short) 81, "application/sgml-open-catalog");
+               addMIMEType((short) 82, "application/sieve");
+               addMIMEType((short) 83, "application/slate");
+               addMIMEType((short) 84, "application/smil", "smi smil");
+               addMIMEType((short) 85, "application/timestamp-query");
+               addMIMEType((short) 86, "application/timestamp-reply");
+               addMIMEType((short) 87, "application/vemmi");
+               addMIMEType((short) 88, "application/whoispp-query");
+               addMIMEType((short) 89, "application/whoispp-response");
+               addMIMEType((short) 90, "application/wita");
+               addMIMEType((short) 91, "application/wordperfect", "wpd");
+               addMIMEType((short) 92, "application/wordperfect5.1", "wp5");
+               addMIMEType((short) 93, "application/x400-bp");
+               addMIMEType((short) 94, "application/xhtml+xml", "xhtml xht");
+               addMIMEType((short) 95, "application/xml", "xml xsl");
+               addMIMEType((short) 96, "application/xml-dtd");
+               addMIMEType((short) 97, "application/xml-external-parsed-entity");
+               addMIMEType((short) 98, "application/zip", "zip");
+               addMIMEType((short) 99, "application/vnd.3M.Post-it-Notes");
+               addMIMEType((short) 100, "application/vnd.accpac.simply.aso");
+               addMIMEType((short) 101, "application/vnd.accpac.simply.imp");
+               addMIMEType((short) 102, "application/vnd.acucobol");
+               addMIMEType((short) 103, "application/vnd.aether.imp");
+               addMIMEType((short) 104, "application/vnd.anser-web-certificate-issue-initiation");
+               addMIMEType((short) 105, "application/vnd.anser-web-funds-transfer-initiation");
+               addMIMEType((short) 106, "application/vnd.audiograph");
+               addMIMEType((short) 107, "application/vnd.bmi");
+               addMIMEType((short) 108, "application/vnd.businessobjects");
+               addMIMEType((short) 109, "application/vnd.canon-cpdl");
+               addMIMEType((short) 110, "application/vnd.canon-lips");
+               addMIMEType((short) 111, "application/vnd.cinderella", "cdy");
+               addMIMEType((short) 112, "application/vnd.claymore");
+               addMIMEType((short) 113, "application/vnd.commerce-battelle");
+               addMIMEType((short) 114, "application/vnd.commonspace");
+               addMIMEType((short) 115, "application/vnd.comsocaller");
+               addMIMEType((short) 116, "application/vnd.contact.cmsg");
+               addMIMEType((short) 117, "application/vnd.cosmocaller");
+               addMIMEType((short) 118, "application/vnd.ctc-posml");
+               addMIMEType((short) 119, "application/vnd.cups-postscript");
+               addMIMEType((short) 120, "application/vnd.cups-raster");
+               addMIMEType((short) 121, "application/vnd.cups-raw");
+               addMIMEType((short) 122, "application/vnd.cybank");
+               addMIMEType((short) 123, "application/vnd.dna");
+               addMIMEType((short) 124, "application/vnd.dpgraph");
+               addMIMEType((short) 125, "application/vnd.dxr");
+               addMIMEType((short) 126, "application/vnd.ecdis-update");
+               addMIMEType((short) 127, "application/vnd.ecowin.chart");
+               addMIMEType((short) 128, "application/vnd.ecowin.filerequest");
+               addMIMEType((short) 129, "application/vnd.ecowin.fileupdate");
+               addMIMEType((short) 130, "application/vnd.ecowin.series");
+               addMIMEType((short) 131, "application/vnd.ecowin.seriesrequest");
+               addMIMEType((short) 132, "application/vnd.ecowin.seriesupdate");
+               addMIMEType((short) 133, "application/vnd.enliven");
+               addMIMEType((short) 134, "application/vnd.epson.esf");
+               addMIMEType((short) 135, "application/vnd.epson.msf");
+               addMIMEType((short) 136, "application/vnd.epson.quickanime");
+               addMIMEType((short) 137, "application/vnd.epson.salt");
+               addMIMEType((short) 138, "application/vnd.epson.ssf");
+               addMIMEType((short) 139, "application/vnd.ericsson.quickcall");
+               addMIMEType((short) 140, "application/vnd.eudora.data");
+               addMIMEType((short) 141, "application/vnd.fdf");
+               addMIMEType((short) 142, "application/vnd.ffsns");
+               addMIMEType((short) 143, "application/vnd.flographit");
+               addMIMEType((short) 144, "application/vnd.framemaker");
+               addMIMEType((short) 145, "application/vnd.fsc.weblaunch");
+               addMIMEType((short) 146, "application/vnd.fujitsu.oasys");
+               addMIMEType((short) 147, "application/vnd.fujitsu.oasys2");
+               addMIMEType((short) 148, "application/vnd.fujitsu.oasys3");
+               addMIMEType((short) 149, "application/vnd.fujitsu.oasysgp");
+               addMIMEType((short) 150, "application/vnd.fujitsu.oasysprs");
+               addMIMEType((short) 151, "application/vnd.fujixerox.ddd");
+               addMIMEType((short) 152, "application/vnd.fujixerox.docuworks");
+               addMIMEType((short) 153, "application/vnd.fujixerox.docuworks.binder");
+               addMIMEType((short) 154, "application/vnd.fut-misnet");
+               addMIMEType((short) 155, "application/vnd.grafeq");
+               addMIMEType((short) 156, "application/vnd.groove-account");
+               addMIMEType((short) 157, "application/vnd.groove-identity-message");
+               addMIMEType((short) 158, "application/vnd.groove-injector");
+               addMIMEType((short) 159, "application/vnd.groove-tool-message");
+               addMIMEType((short) 160, "application/vnd.groove-tool-template");
+               addMIMEType((short) 161, "application/vnd.groove-vcard");
+               addMIMEType((short) 162, "application/vnd.hhe.lesson-player");
+               addMIMEType((short) 163, "application/vnd.hp-HPGL");
+               addMIMEType((short) 164, "application/vnd.hp-PCL");
+               addMIMEType((short) 165, "application/vnd.hp-PCLXL");
+               addMIMEType((short) 166, "application/vnd.hp-hpid");
+               addMIMEType((short) 167, "application/vnd.hp-hps");
+               addMIMEType((short) 168, "application/vnd.httphone");
+               addMIMEType((short) 169, "application/vnd.hzn-3d-crossword");
+               addMIMEType((short) 170, "application/vnd.ibm.MiniPay");
+               addMIMEType((short) 171, "application/vnd.ibm.afplinedata");
+               addMIMEType((short) 172, "application/vnd.ibm.modcap");
+               addMIMEType((short) 173, "application/vnd.informix-visionary");
+               addMIMEType((short) 174, "application/vnd.intercon.formnet");
+               addMIMEType((short) 175, "application/vnd.intertrust.digibox");
+               addMIMEType((short) 176, "application/vnd.intertrust.nncp");
+               addMIMEType((short) 177, "application/vnd.intu.qbo");
+               addMIMEType((short) 178, "application/vnd.intu.qfx");
+               addMIMEType((short) 179, "application/vnd.irepository.package+xml");
+               addMIMEType((short) 180, "application/vnd.is-xpr");
+               addMIMEType((short) 181, "application/vnd.japannet-directory-service");
+               addMIMEType((short) 182, "application/vnd.japannet-jpnstore-wakeup");
+               addMIMEType((short) 183, "application/vnd.japannet-payment-wakeup");
+               addMIMEType((short) 184, "application/vnd.japannet-registration");
+               addMIMEType((short) 185, "application/vnd.japannet-registration-wakeup");
+               addMIMEType((short) 186, "application/vnd.japannet-setstore-wakeup");
+               addMIMEType((short) 187, "application/vnd.japannet-verification");
+               addMIMEType((short) 188, "application/vnd.japannet-verification-wakeup");
+               addMIMEType((short) 189, "application/vnd.koan");
+               addMIMEType((short) 190, "application/vnd.lotus-1-2-3");
+               addMIMEType((short) 191, "application/vnd.lotus-approach");
+               addMIMEType((short) 192, "application/vnd.lotus-freelance");
+               addMIMEType((short) 193, "application/vnd.lotus-notes");
+               addMIMEType((short) 194, "application/vnd.lotus-organizer");
+               addMIMEType((short) 195, "application/vnd.lotus-screencam");
+               addMIMEType((short) 196, "application/vnd.lotus-wordpro");
+               addMIMEType((short) 197, "application/vnd.mcd");
+               addMIMEType((short) 198, "application/vnd.mediastation.cdkey");
+               addMIMEType((short) 199, "application/vnd.meridian-slingshot");
+               addMIMEType((short) 200, "application/vnd.mif");
+               addMIMEType((short) 201, "application/vnd.minisoft-hp3000-save");
+               addMIMEType((short) 202, "application/vnd.mitsubishi.misty-guard.trustweb");
+               addMIMEType((short) 203, "application/vnd.mobius.daf");
+               addMIMEType((short) 204, "application/vnd.mobius.dis");
+               addMIMEType((short) 205, "application/vnd.mobius.msl");
+               addMIMEType((short) 206, "application/vnd.mobius.plc");
+               addMIMEType((short) 207, "application/vnd.mobius.txf");
+               addMIMEType((short) 208, "application/vnd.motorola.flexsuite");
+               addMIMEType((short) 209, "application/vnd.motorola.flexsuite.adsi");
+               addMIMEType((short) 210, "application/vnd.motorola.flexsuite.fis");
+               addMIMEType((short) 211, "application/vnd.motorola.flexsuite.gotap");
+               addMIMEType((short) 212, "application/vnd.motorola.flexsuite.kmr");
+               addMIMEType((short) 213, "application/vnd.motorola.flexsuite.ttc");
+               addMIMEType((short) 214, "application/vnd.motorola.flexsuite.wem");
+               addMIMEType((short) 215, "application/vnd.mozilla.xul+xml", "xul");
+               addMIMEType((short) 216, "application/vnd.ms-artgalry");
+               addMIMEType((short) 217, "application/vnd.ms-asf");
+               addMIMEType((short) 218, "application/vnd.ms-excel", "xls xlb xlt");
+               addMIMEType((short) 219, "application/vnd.ms-lrm");
+               addMIMEType((short) 220, "application/vnd.ms-pki.seccat", "cat");
+               addMIMEType((short) 221, "application/vnd.ms-pki.stl", "stl");
+               addMIMEType((short) 222, "application/vnd.ms-powerpoint", "ppt pps");
+               addMIMEType((short) 223, "application/vnd.ms-project");
+               addMIMEType((short) 224, "application/vnd.ms-tnef");
+               addMIMEType((short) 225, "application/vnd.ms-works");
+               addMIMEType((short) 226, "application/vnd.mseq");
+               addMIMEType((short) 227, "application/vnd.msign");
+               addMIMEType((short) 228, "application/vnd.music-niff");
+               addMIMEType((short) 229, "application/vnd.musician");
+               addMIMEType((short) 230, "application/vnd.netfpx");
+               addMIMEType((short) 231, "application/vnd.noblenet-directory");
+               addMIMEType((short) 232, "application/vnd.noblenet-sealer");
+               addMIMEType((short) 233, "application/vnd.noblenet-web");
+               addMIMEType((short) 234, "application/vnd.novadigm.EDM");
+               addMIMEType((short) 235, "application/vnd.novadigm.EDX");
+               addMIMEType((short) 236, "application/vnd.novadigm.EXT");
+               addMIMEType((short) 237, "application/vnd.oasis.opendocument.chart", "odc");
+               addMIMEType((short) 238, "application/vnd.oasis.opendocument.database", "odb");
+               addMIMEType((short) 239, "application/vnd.oasis.opendocument.formula", "odf");
+               addMIMEType((short) 240, "application/vnd.oasis.opendocument.graphics", "odg");
+               addMIMEType((short) 241, "application/vnd.oasis.opendocument.graphics-template", "otg");
+               addMIMEType((short) 242, "application/vnd.oasis.opendocument.image", "odi");
+               addMIMEType((short) 243, "application/vnd.oasis.opendocument.presentation", "odp");
+               addMIMEType((short) 244, "application/vnd.oasis.opendocument.presentation-template", "otp");
+               addMIMEType((short) 245, "application/vnd.oasis.opendocument.spreadsheet", "ods");
+               addMIMEType((short) 246, "application/vnd.oasis.opendocument.spreadsheet-template", "ots");
+               addMIMEType((short) 247, "application/vnd.oasis.opendocument.text", "odt");
+               addMIMEType((short) 248, "application/vnd.oasis.opendocument.text-master", "odm");
+               addMIMEType((short) 249, "application/vnd.oasis.opendocument.text-template", "ott");
+               addMIMEType((short) 250, "application/vnd.oasis.opendocument.text-web", "oth");
+               addMIMEType((short) 251, "application/vnd.osa.netdeploy");
+               addMIMEType((short) 252, "application/vnd.palm");
+               addMIMEType((short) 253, "application/vnd.pg.format");
+               addMIMEType((short) 254, "application/vnd.pg.osasli");
+               addMIMEType((short) 255, "application/vnd.powerbuilder6");
+               addMIMEType((short) 256, "application/vnd.powerbuilder6-s");
+               addMIMEType((short) 257, "application/vnd.powerbuilder7");
+               addMIMEType((short) 258, "application/vnd.powerbuilder7-s");
+               addMIMEType((short) 259, "application/vnd.powerbuilder75");
+               addMIMEType((short) 260, "application/vnd.powerbuilder75-s");
+               addMIMEType((short) 261, "application/vnd.previewsystems.box");
+               addMIMEType((short) 262, "application/vnd.publishare-delta-tree");
+               addMIMEType((short) 263, "application/vnd.pvi.ptid1");
+               addMIMEType((short) 264, "application/vnd.pwg-xhtml-print+xml");
+               addMIMEType((short) 265, "application/vnd.rapid");
+               addMIMEType((short) 266, "application/vnd.rim.cod", "cod");
+               addMIMEType((short) 267, "application/vnd.s3sms");
+               addMIMEType((short) 268, "application/vnd.seemail");
+               addMIMEType((short) 269, "application/vnd.shana.informed.formdata");
+               addMIMEType((short) 270, "application/vnd.shana.informed.formtemplate");
+               addMIMEType((short) 271, "application/vnd.shana.informed.interchange");
+               addMIMEType((short) 272, "application/vnd.shana.informed.package");
+               addMIMEType((short) 273, "application/vnd.smaf", "mmf");
+               addMIMEType((short) 274, "application/vnd.sss-cod");
+               addMIMEType((short) 275, "application/vnd.sss-dtf");
+               addMIMEType((short) 276, "application/vnd.sss-ntf");
+               addMIMEType((short) 277, "application/vnd.stardivision.calc", "sdc");
+               addMIMEType((short) 278, "application/vnd.stardivision.draw", "sda");
+               addMIMEType((short) 279, "application/vnd.stardivision.impress", "sdd sdp");
+               addMIMEType((short) 280, "application/vnd.stardivision.math", "smf");
+               addMIMEType((short) 281, "application/vnd.stardivision.writer", "sdw vor");
+               addMIMEType((short) 282, "application/vnd.stardivision.writer-global", "sgl");
+               addMIMEType((short) 283, "application/vnd.street-stream");
+               addMIMEType((short) 284, "application/vnd.sun.xml.calc", "sxc");
+               addMIMEType((short) 285, "application/vnd.sun.xml.calc.template", "stc");
+               addMIMEType((short) 286, "application/vnd.sun.xml.draw", "sxd");
+               addMIMEType((short) 287, "application/vnd.sun.xml.draw.template", "std");
+               addMIMEType((short) 288, "application/vnd.sun.xml.impress", "sxi");
+               addMIMEType((short) 289, "application/vnd.sun.xml.impress.template", "sti");
+               addMIMEType((short) 290, "application/vnd.sun.xml.math", "sxm");
+               addMIMEType((short) 291, "application/vnd.sun.xml.writer", "sxw");
+               addMIMEType((short) 292, "application/vnd.sun.xml.writer.global", "sxg");
+               addMIMEType((short) 293, "application/vnd.sun.xml.writer.template", "stw");
+               addMIMEType((short) 294, "application/vnd.svd");
+               addMIMEType((short) 295, "application/vnd.swiftview-ics");
+               addMIMEType((short) 296, "application/vnd.symbian.install", "sis");
+               addMIMEType((short) 297, "application/vnd.triscape.mxs");
+               addMIMEType((short) 298, "application/vnd.trueapp");
+               addMIMEType((short) 299, "application/vnd.truedoc");
+               addMIMEType((short) 300, "application/vnd.tve-trigger");
+               addMIMEType((short) 301, "application/vnd.ufdl");
+               addMIMEType((short) 302, "application/vnd.uplanet.alert");
+               addMIMEType((short) 303, "application/vnd.uplanet.alert-wbxml");
+               addMIMEType((short) 304, "application/vnd.uplanet.bearer-choice");
+               addMIMEType((short) 305, "application/vnd.uplanet.bearer-choice-wbxml");
+               addMIMEType((short) 306, "application/vnd.uplanet.cacheop");
+               addMIMEType((short) 307, "application/vnd.uplanet.cacheop-wbxml");
+               addMIMEType((short) 308, "application/vnd.uplanet.channel");
+               addMIMEType((short) 309, "application/vnd.uplanet.channel-wbxml");
+               addMIMEType((short) 310, "application/vnd.uplanet.list");
+               addMIMEType((short) 311, "application/vnd.uplanet.list-wbxml");
+               addMIMEType((short) 312, "application/vnd.uplanet.listcmd");
+               addMIMEType((short) 313, "application/vnd.uplanet.listcmd-wbxml");
+               addMIMEType((short) 314, "application/vnd.uplanet.signal");
+               addMIMEType((short) 315, "application/vnd.vcx");
+               addMIMEType((short) 316, "application/vnd.vectorworks");
+               addMIMEType((short) 317, "application/vnd.vidsoft.vidconference");
+               addMIMEType((short) 318, "application/vnd.visio", "vsd");
+               addMIMEType((short) 319, "application/vnd.vividence.scriptfile");
+               addMIMEType((short) 320, "application/vnd.wap.sic");
+               addMIMEType((short) 321, "application/vnd.wap.slc");
+               addMIMEType((short) 322, "application/vnd.wap.wbxml", "wbxml");
+               addMIMEType((short) 323, "application/vnd.wap.wmlc", "wmlc");
+               addMIMEType((short) 324, "application/vnd.wap.wmlscriptc", "wmlsc");
+               addMIMEType((short) 325, "application/vnd.webturbo");
+               addMIMEType((short) 326, "application/vnd.wrq-hp3000-labelled");
+               addMIMEType((short) 327, "application/vnd.wt.stf");
+               addMIMEType((short) 328, "application/vnd.xara");
+               addMIMEType((short) 329, "application/vnd.xfdl");
+               addMIMEType((short) 330, "application/vnd.yellowriver-custom-menu");
+               addMIMEType((short) 331, "application/x-123", "wk");
+               addMIMEType((short) 332, "application/x-abiword", "abw");
+               addMIMEType((short) 333, "application/x-apple-diskimage", "dmg");
+               addMIMEType((short) 334, "application/x-bcpio", "bcpio");
+               addMIMEType((short) 335, "application/x-bittorrent", "torrent");
+               addMIMEType((short) 336, "application/x-cdf", "cdf");
+               addMIMEType((short) 337, "application/x-cdlink", "vcd");
+               addMIMEType((short) 338, "application/x-chess-pgn", "pgn");
+               addMIMEType((short) 339, "application/x-core");
+               addMIMEType((short) 340, "application/x-cpio", "cpio");
+               addMIMEType((short) 341, "application/x-csh", "csh");
+               addMIMEType((short) 342, "application/x-debian-package", "deb udeb");
+               addMIMEType((short) 343, "application/x-director", "dcr dir dxr");
+               addMIMEType((short) 344, "application/x-dms", "dms");
+               addMIMEType((short) 345, "application/x-doom", "wad");
+               addMIMEType((short) 346, "application/x-dvi", "dvi");
+               addMIMEType((short) 347, "application/x-executable");
+               addMIMEType((short) 348, "application/x-flac", "flac");
+               addMIMEType((short) 349, "application/x-font", "pfa pfb gsf pcf pcf.Z");
+               addMIMEType((short) 350, "application/x-freemind", "mm");
+               addMIMEType((short) 351, "application/x-futuresplash", "spl");
+               addMIMEType((short) 352, "application/x-gnumeric", "gnumeric");
+               addMIMEType((short) 353, "application/x-go-sgf", "sgf");
+               addMIMEType((short) 354, "application/x-graphing-calculator", "gcf");
+               addMIMEType((short) 355, "application/x-gtar", "gtar tgz taz");
+               addMIMEType((short) 356, "application/x-hdf", "hdf");
+               addMIMEType((short) 357, "application/x-httpd-php", "phtml pht php");
+               addMIMEType((short) 358, "application/x-httpd-php-source", "phps");
+               addMIMEType((short) 359, "application/x-httpd-php3", "php3");
+               addMIMEType((short) 360, "application/x-httpd-php3-preprocessed", "php3p");
+               addMIMEType((short) 361, "application/x-httpd-php4", "php4");
+               addMIMEType((short) 362, "application/x-ica", "ica");
+               addMIMEType((short) 363, "application/x-internet-signup", "ins isp");
+               addMIMEType((short) 364, "application/x-iphone", "iii");
+               addMIMEType((short) 365, "application/x-iso9660-image", "iso");
+               addMIMEType((short) 366, "application/x-java-applet");
+               addMIMEType((short) 367, "application/x-java-bean");
+               addMIMEType((short) 368, "application/x-java-jnlp-file", "jnlp");
+               addMIMEType((short) 369, "application/x-javascript", "js");
+               addMIMEType((short) 370, "application/x-jmol", "jmz");
+               addMIMEType((short) 371, "application/x-kchart", "chrt");
+               addMIMEType((short) 372, "application/x-kdelnk");
+               addMIMEType((short) 373, "application/x-killustrator", "kil");
+               addMIMEType((short) 374, "application/x-koan", "skp skd skt skm");
+               addMIMEType((short) 375, "application/x-kpresenter", "kpr kpt");
+               addMIMEType((short) 376, "application/x-kspread", "ksp");
+               addMIMEType((short) 377, "application/x-kword", "kwd kwt");
+               addMIMEType((short) 378, "application/x-latex", "latex");
+               addMIMEType((short) 379, "application/x-lha", "lha");
+               addMIMEType((short) 380, "application/x-lzh", "lzh");
+               addMIMEType((short) 381, "application/x-lzx", "lzx");
+               addMIMEType((short) 382, "application/x-maker", "frm maker frame fm fb book fbdoc");
+               addMIMEType((short) 383, "application/x-mif", "mif");
+               addMIMEType((short) 384, "application/x-ms-wmd", "wmd");
+               addMIMEType((short) 385, "application/x-ms-wmz", "wmz");
+               addMIMEType((short) 386, "application/x-msdos-program", "com exe bat dll");
+               addMIMEType((short) 387, "application/x-msi", "msi");
+               addMIMEType((short) 388, "application/x-netcdf", "nc");
+               addMIMEType((short) 389, "application/x-ns-proxy-autoconfig", "pac");
+               addMIMEType((short) 390, "application/x-nwc", "nwc");
+               addMIMEType((short) 391, "application/x-object", "o");
+               addMIMEType((short) 392, "application/x-oz-application", "oza");
+               addMIMEType((short) 393, "application/x-pkcs7-certreqresp", "p7r");
+               addMIMEType((short) 394, "application/x-pkcs7-crl", "crl");
+               addMIMEType((short) 395, "application/x-python-code", "pyc pyo");
+               addMIMEType((short) 396, "application/x-quicktimeplayer", "qtl");
+               addMIMEType((short) 397, "application/x-redhat-package-manager", "rpm");
+               addMIMEType((short) 398, "application/x-rx");
+               addMIMEType((short) 399, "application/x-sh", "sh");
+               addMIMEType((short) 400, "application/x-shar", "shar");
+               addMIMEType((short) 401, "application/x-shellscript");
+               addMIMEType((short) 402, "application/x-shockwave-flash", "swf swfl");
+               addMIMEType((short) 403, "application/x-stuffit", "sit");
+               addMIMEType((short) 404, "application/x-sv4cpio", "sv4cpio");
+               addMIMEType((short) 405, "application/x-sv4crc", "sv4crc");
+               addMIMEType((short) 406, "application/x-tar", "tar");
+               addMIMEType((short) 407, "application/x-tcl", "tcl");
+               addMIMEType((short) 408, "application/x-tex-gf", "gf");
+               addMIMEType((short) 409, "application/x-tex-pk", "pk");
+               addMIMEType((short) 410, "application/x-texinfo", "texinfo texi");
+               addMIMEType((short) 411, "application/x-trash", "~ % bak old sik");
+               addMIMEType((short) 412, "application/x-troff", "t tr roff");
+               addMIMEType((short) 413, "application/x-troff-man", "man");
+               addMIMEType((short) 414, "application/x-troff-me", "me");
+               addMIMEType((short) 415, "application/x-troff-ms", "ms");
+               addMIMEType((short) 416, "application/x-ustar", "ustar");
+               addMIMEType((short) 417, "application/x-videolan");
+               addMIMEType((short) 418, "application/x-wais-source", "src");
+               addMIMEType((short) 419, "application/x-wingz", "wz");
+               addMIMEType((short) 420, "application/x-x509-ca-cert", "crt");
+               addMIMEType((short) 421, "application/x-xcf", "xcf");
+               addMIMEType((short) 422, "application/x-xfig", "fig");
+               addMIMEType((short) 423, "application/x-xpinstall", "xpi");
+               addMIMEType((short) 424, "audio/32kadpcm");
+               addMIMEType((short) 425, "audio/basic", "au snd");
+               addMIMEType((short) 426, "audio/g.722.1");
+               addMIMEType((short) 427, "audio/l16");
+               addMIMEType((short) 428, "audio/midi", "mid midi kar");
+               addMIMEType((short) 429, "audio/mp4a-latm");
+               addMIMEType((short) 430, "audio/mpa-robust");
+               addMIMEType((short) 431, "audio/mpeg", "mpga mpega mp2 mp3 m4a");
+               addMIMEType((short) 432, "audio/mpegurl", "m3u");
+               addMIMEType((short) 433, "audio/parityfec");
+               addMIMEType((short) 434, "audio/prs.sid", "sid");
+               addMIMEType((short) 435, "audio/telephone-event");
+               addMIMEType((short) 436, "audio/tone");
+               addMIMEType((short) 437, "audio/vnd.cisco.nse");
+               addMIMEType((short) 438, "audio/vnd.cns.anp1");
+               addMIMEType((short) 439, "audio/vnd.cns.inf1");
+               addMIMEType((short) 440, "audio/vnd.digital-winds");
+               addMIMEType((short) 441, "audio/vnd.everad.plj");
+               addMIMEType((short) 442, "audio/vnd.lucent.voice");
+               addMIMEType((short) 443, "audio/vnd.nortel.vbk");
+               addMIMEType((short) 444, "audio/vnd.nuera.ecelp4800");
+               addMIMEType((short) 445, "audio/vnd.nuera.ecelp7470");
+               addMIMEType((short) 446, "audio/vnd.nuera.ecelp9600");
+               addMIMEType((short) 447, "audio/vnd.octel.sbc");
+               addMIMEType((short) 448, "audio/vnd.qcelp");
+               addMIMEType((short) 449, "audio/vnd.rhetorex.32kadpcm");
+               addMIMEType((short) 450, "audio/vnd.vmx.cvsd");
+               addMIMEType((short) 451, "audio/x-aiff", "aif aiff aifc");
+               addMIMEType((short) 452, "audio/x-gsm", "gsm");
+               addMIMEType((short) 453, "audio/x-mpegurl", "m3u");
+               addMIMEType((short) 454, "audio/x-ms-wma", "wma");
+               addMIMEType((short) 455, "audio/x-ms-wax", "wax");
+               addMIMEType((short) 456, "audio/x-pn-realaudio-plugin");
+               addMIMEType((short) 457, "audio/x-pn-realaudio", "ra rm ram");
+               addMIMEType((short) 458, "audio/x-realaudio", "ra");
+               addMIMEType((short) 459, "audio/x-scpls", "pls");
+               addMIMEType((short) 460, "audio/x-sd2", "sd2");
+               addMIMEType((short) 461, "audio/x-wav", "wav");
+               addMIMEType((short) 462, "chemical/x-alchemy", "alc");
+               addMIMEType((short) 463, "chemical/x-cache", "cac cache");
+               addMIMEType((short) 464, "chemical/x-cache-csf", "csf");
+               addMIMEType((short) 465, "chemical/x-cactvs-binary", "cbin cascii ctab");
+               addMIMEType((short) 466, "chemical/x-cdx", "cdx");
+               addMIMEType((short) 467, "chemical/x-cerius", "cer");
+               addMIMEType((short) 468, "chemical/x-chem3d", "c3d");
+               addMIMEType((short) 469, "chemical/x-chemdraw", "chm");
+               addMIMEType((short) 470, "chemical/x-cif", "cif");
+               addMIMEType((short) 471, "chemical/x-cmdf", "cmdf");
+               addMIMEType((short) 472, "chemical/x-cml", "cml");
+               addMIMEType((short) 473, "chemical/x-compass", "cpa");
+               addMIMEType((short) 474, "chemical/x-crossfire", "bsd");
+               addMIMEType((short) 475, "chemical/x-csml", "csml csm");
+               addMIMEType((short) 476, "chemical/x-ctx", "ctx");
+               addMIMEType((short) 477, "chemical/x-cxf", "cxf cef");
+               addMIMEType((short) 478, "chemical/x-embl-dl-nucleotide", "emb embl");
+               addMIMEType((short) 479, "chemical/x-galactic-spc", "spc");
+               addMIMEType((short) 480, "chemical/x-gamess-input", "inp gam gamin");
+               addMIMEType((short) 481, "chemical/x-gaussian-checkpoint", "fch fchk");
+               addMIMEType((short) 482, "chemical/x-gaussian-cube", "cub");
+               addMIMEType((short) 483, "chemical/x-gaussian-input", "gau gjc gjf");
+               addMIMEType((short) 484, "chemical/x-gaussian-log", "gal");
+               addMIMEType((short) 485, "chemical/x-gcg8-sequence", "gcg");
+               addMIMEType((short) 486, "chemical/x-genbank", "gen");
+               addMIMEType((short) 487, "chemical/x-hin", "hin");
+               addMIMEType((short) 488, "chemical/x-isostar", "istr ist");
+               addMIMEType((short) 489, "chemical/x-jcamp-dx", "jdx dx");
+               addMIMEType((short) 490, "chemical/x-kinemage", "kin");
+               addMIMEType((short) 491, "chemical/x-macmolecule", "mcm");
+               addMIMEType((short) 492, "chemical/x-macromodel-input", "mmd mmod");
+               addMIMEType((short) 493, "chemical/x-mdl-molfile", "mol");
+               addMIMEType((short) 494, "chemical/x-mdl-rdfile", "rd");
+               addMIMEType((short) 495, "chemical/x-mdl-rxnfile", "rxn");
+               addMIMEType((short) 496, "chemical/x-mdl-sdfile", "sd sdf");
+               addMIMEType((short) 497, "chemical/x-mdl-tgf", "tgf");
+               addMIMEType((short) 498, "chemical/x-mmcif", "mcif");
+               addMIMEType((short) 499, "chemical/x-mol2", "mol2");
+               addMIMEType((short) 500, "chemical/x-molconn-Z", "b");
+               addMIMEType((short) 501, "chemical/x-mopac-graph", "gpt");
+               addMIMEType((short) 502, "chemical/x-mopac-input", "mop mopcrt mpc dat zmt");
+               addMIMEType((short) 503, "chemical/x-mopac-out", "moo");
+               addMIMEType((short) 504, "chemical/x-mopac-vib", "mvb");
+               addMIMEType((short) 505, "chemical/x-ncbi-asn1", "asn");
+               addMIMEType((short) 506, "chemical/x-ncbi-asn1-ascii", "prt ent");
+               addMIMEType((short) 507, "chemical/x-ncbi-asn1-binary", "val aso");
+               addMIMEType((short) 508, "chemical/x-ncbi-asn1-spec", "asn");
+               addMIMEType((short) 509, "chemical/x-pdb", "pdb ent");
+               addMIMEType((short) 510, "chemical/x-rosdal", "ros");
+               addMIMEType((short) 511, "chemical/x-swissprot", "sw");
+               addMIMEType((short) 512, "chemical/x-vamas-iso14976", "vms");
+               addMIMEType((short) 513, "chemical/x-vmd", "vmd");
+               addMIMEType((short) 514, "chemical/x-xtel", "xtel");
+               addMIMEType((short) 515, "chemical/x-xyz", "xyz");
+               addMIMEType((short) 516, "image/cgm");
+               addMIMEType((short) 517, "image/g3fax");
+               addMIMEType((short) 518, "image/gif", "gif");
+               addMIMEType((short) 519, "image/ief", "ief");
+               addMIMEType((short) 520, "image/jpeg", "jpeg jpg jpe");
+               addMIMEType((short) 521, "image/naplps");
+               addMIMEType((short) 522, "image/pcx", "pcx");
+               addMIMEType((short) 523, "image/png", "png");
+               addMIMEType((short) 524, "image/prs.btif");
+               addMIMEType((short) 525, "image/prs.pti");
+               addMIMEType((short) 526, "image/svg+xml", "svg svgz");
+               addMIMEType((short) 527, "image/tiff", "tiff tif");
+               addMIMEType((short) 528, "image/vnd.cns.inf2");
+               addMIMEType((short) 529, "image/vnd.djvu", "djvu djv");
+               addMIMEType((short) 530, "image/vnd.dwg");
+               addMIMEType((short) 531, "image/vnd.dxf");
+               addMIMEType((short) 532, "image/vnd.fastbidsheet");
+               addMIMEType((short) 533, "image/vnd.fpx");
+               addMIMEType((short) 534, "image/vnd.fst");
+               addMIMEType((short) 535, "image/vnd.fujixerox.edmics-mmr");
+               addMIMEType((short) 536, "image/vnd.fujixerox.edmics-rlc");
+               addMIMEType((short) 537, "image/vnd.mix");
+               addMIMEType((short) 538, "image/vnd.net-fpx");
+               addMIMEType((short) 539, "image/vnd.svf");
+               addMIMEType((short) 540, "image/vnd.wap.wbmp", "wbmp");
+               addMIMEType((short) 541, "image/vnd.xiff");
+               addMIMEType((short) 542, "image/x-cmu-raster", "ras");
+               addMIMEType((short) 543, "image/x-coreldraw", "cdr");
+               addMIMEType((short) 544, "image/x-coreldrawpattern", "pat");
+               addMIMEType((short) 545, "image/x-coreldrawtemplate", "cdt");
+               addMIMEType((short) 546, "image/x-corelphotopaint", "cpt");
+               addMIMEType((short) 547, "image/x-icon", "ico");
+               addMIMEType((short) 548, "image/x-jg", "art");
+               addMIMEType((short) 549, "image/x-jng", "jng");
+               addMIMEType((short) 550, "image/x-ms-bmp", "bmp");
+               addMIMEType((short) 551, "image/x-photoshop", "psd");
+               addMIMEType((short) 552, "image/x-portable-anymap", "pnm");
+               addMIMEType((short) 553, "image/x-portable-bitmap", "pbm");
+               addMIMEType((short) 554, "image/x-portable-graymap", "pgm");
+               addMIMEType((short) 555, "image/x-portable-pixmap", "ppm");
+               addMIMEType((short) 556, "image/x-rgb", "rgb");
+               addMIMEType((short) 557, "image/x-xbitmap", "xbm");
+               addMIMEType((short) 558, "image/x-xpixmap", "xpm");
+               addMIMEType((short) 559, "image/x-xwindowdump", "xwd");
+               addMIMEType((short) 560, "inode/chardevice");
+               addMIMEType((short) 561, "inode/blockdevice");
+               addMIMEType((short) 562, "inode/directory-locked");
+               addMIMEType((short) 563, "inode/directory");
+               addMIMEType((short) 564, "inode/fifo");
+               addMIMEType((short) 565, "inode/socket");
+               addMIMEType((short) 566, "message/delivery-status");
+               addMIMEType((short) 567, "message/disposition-notification");
+               addMIMEType((short) 568, "message/external-body");
+               addMIMEType((short) 569, "message/http");
+               addMIMEType((short) 570, "message/s-http");
+               addMIMEType((short) 571, "message/news");
+               addMIMEType((short) 572, "message/partial");
+               addMIMEType((short) 573, "message/rfc822");
+               addMIMEType((short) 574, "model/iges", "igs iges");
+               addMIMEType((short) 575, "model/mesh", "msh mesh silo");
+               addMIMEType((short) 576, "model/vnd.dwf");
+               addMIMEType((short) 577, "model/vnd.flatland.3dml");
+               addMIMEType((short) 578, "model/vnd.gdl");
+               addMIMEType((short) 579, "model/vnd.gs-gdl");
+               addMIMEType((short) 580, "model/vnd.gtw");
+               addMIMEType((short) 581, "model/vnd.mts");
+               addMIMEType((short) 582, "model/vnd.vtu");
+               addMIMEType((short) 583, "model/vrml", "wrl vrml");
+               addMIMEType((short) 584, "multipart/alternative");
+               addMIMEType((short) 585, "multipart/appledouble");
+               addMIMEType((short) 586, "multipart/byteranges");
+               addMIMEType((short) 587, "multipart/digest");
+               addMIMEType((short) 588, "multipart/encrypted");
+               addMIMEType((short) 589, "multipart/form-data");
+               addMIMEType((short) 590, "multipart/header-set");
+               addMIMEType((short) 591, "multipart/mixed");
+               addMIMEType((short) 592, "multipart/parallel");
+               addMIMEType((short) 593, "multipart/related");
+               addMIMEType((short) 594, "multipart/report");
+               addMIMEType((short) 595, "multipart/signed");
+               addMIMEType((short) 596, "multipart/voice-message");
+               addMIMEType((short) 597, "text/calendar", "ics icz");
+               addMIMEType((short) 598, "text/comma-separated-values", "csv");
+               addMIMEType((short) 599, "text/css", "css");
+               addMIMEType((short) 600, "text/directory");
+               addMIMEType((short) 601, "text/english");
+               addMIMEType((short) 602, "text/enriched");
+               addMIMEType((short) 603, "text/h323", "323");
+               addMIMEType((short) 604, "text/html", "html htm shtml");
+               addMIMEType((short) 605, "text/iuls", "uls");
+               addMIMEType((short) 606, "text/mathml", "mml");
+               addMIMEType((short) 607, "text/parityfec");
+               addMIMEType((short) 608, "text/plain", "asc txt text diff pot");
+               addMIMEType((short) 609, "text/prs.lines.tag");
+               addMIMEType((short) 610, "text/x-psp", "psp");
+               addMIMEType((short) 611, "text/rfc822-headers");
+               addMIMEType((short) 612, "text/richtext", "rtx");
+               addMIMEType((short) 613, "text/rtf", "rtf");
+               addMIMEType((short) 614, "text/scriptlet", "sct wsc");
+               addMIMEType((short) 615, "text/t140");
+               addMIMEType((short) 616, "text/texmacs", "tm ts");
+               addMIMEType((short) 617, "text/tab-separated-values", "tsv");
+               addMIMEType((short) 618, "text/uri-list");
+               addMIMEType((short) 619, "text/vnd.abc");
+               addMIMEType((short) 620, "text/vnd.curl");
+               addMIMEType((short) 621, "text/vnd.DMClientScript");
+               addMIMEType((short) 622, "text/vnd.flatland.3dml");
+               addMIMEType((short) 623, "text/vnd.fly");
+               addMIMEType((short) 624, "text/vnd.fmi.flexstor");
+               addMIMEType((short) 625, "text/vnd.in3d.3dml");
+               addMIMEType((short) 626, "text/vnd.in3d.spot");
+               addMIMEType((short) 627, "text/vnd.IPTC.NewsML");
+               addMIMEType((short) 628, "text/vnd.IPTC.NITF");
+               addMIMEType((short) 629, "text/vnd.latex-z");
+               addMIMEType((short) 630, "text/vnd.motorola.reflex");
+               addMIMEType((short) 631, "text/vnd.ms-mediapackage");
+               addMIMEType((short) 632, "text/vnd.sun.j2me.app-descriptor", "jad");
+               addMIMEType((short) 633, "text/vnd.wap.si");
+               addMIMEType((short) 634, "text/vnd.wap.sl");
+               addMIMEType((short) 635, "text/vnd.wap.wml", "wml");
+               addMIMEType((short) 636, "text/vnd.wap.wmlscript", "wmls");
+               addMIMEType((short) 637, "text/x-bibtex", "bib");
+               addMIMEType((short) 638, "text/x-c++hdr", "h++ hpp hxx hh");
+               addMIMEType((short) 639, "text/x-c++src", "c++ cpp cxx cc");
+               addMIMEType((short) 640, "text/x-chdr", "h");
+               addMIMEType((short) 641, "text/x-crontab");
+               addMIMEType((short) 642, "text/x-csh", "csh");
+               addMIMEType((short) 643, "text/x-csrc", "c");
+               addMIMEType((short) 644, "text/x-haskell", "hs");
+               addMIMEType((short) 645, "text/x-java", "java");
+               addMIMEType((short) 646, "text/x-literate-haskell", "lhs");
+               addMIMEType((short) 647, "text/x-makefile");
+               addMIMEType((short) 648, "text/x-moc", "moc");
+               addMIMEType((short) 649, "text/x-pascal", "p pas");
+               addMIMEType((short) 650, "text/x-pcs-gcd", "gcd");
+               addMIMEType((short) 651, "text/x-perl", "pl pm");
+               addMIMEType((short) 652, "text/x-python", "py");
+               addMIMEType((short) 653, "text/x-server-parsed-html");
+               addMIMEType((short) 654, "text/x-setext", "etx");
+               addMIMEType((short) 655, "text/x-sh", "sh");
+               addMIMEType((short) 656, "text/x-tcl", "tcl tk");
+               addMIMEType((short) 657, "text/x-tex", "tex ltx sty cls");
+               addMIMEType((short) 658, "text/x-vcalendar", "vcs");
+               addMIMEType((short) 659, "text/x-vcard", "vcf");
+               addMIMEType((short) 660, "video/dl", "dl");
+               addMIMEType((short) 661, "video/dv", "dif dv");
+               addMIMEType((short) 662, "video/fli", "fli");
+               addMIMEType((short) 663, "video/gl", "gl");
+               addMIMEType((short) 664, "video/mpeg", "mpeg mpg mpe");
+               addMIMEType((short) 665, "video/mp4", "mp4");
+               addMIMEType((short) 666, "video/quicktime", "qt mov");
+               addMIMEType((short) 667, "video/mp4v-es");
+               addMIMEType((short) 668, "video/parityfec");
+               addMIMEType((short) 669, "video/pointer");
+               addMIMEType((short) 670, "video/vnd.fvt");
+               addMIMEType((short) 671, "video/vnd.motorola.video");
+               addMIMEType((short) 672, "video/vnd.motorola.videop");
+               addMIMEType((short) 673, "video/vnd.mpegurl", "mxu");
+               addMIMEType((short) 674, "video/vnd.mts");
+               addMIMEType((short) 675, "video/vnd.nokia.interleaved-multimedia");
+               addMIMEType((short) 676, "video/vnd.vivo");
+               addMIMEType((short) 677, "video/x-la-asf", "lsf lsx");
+               addMIMEType((short) 678, "video/x-mng", "mng");
+               addMIMEType((short) 679, "video/x-ms-asf", "asf asx");
+               addMIMEType((short) 680, "video/x-ms-wm", "wm");
+               addMIMEType((short) 681, "video/x-ms-wmv", "wmv");
+               addMIMEType((short) 682, "video/x-ms-wmx", "wmx");
+               addMIMEType((short) 683, "video/x-ms-wvx", "wvx");
+               addMIMEType((short) 684, "video/x-msvideo", "avi");
+               addMIMEType((short) 685, "video/x-sgi-movie", "movie");
+               addMIMEType((short) 686, "x-conference/x-cooltalk", "ice");
+               addMIMEType((short) 687, "x-world/x-vrml", "vrm vrml wrl");
+       }
+       
+       /** Guess a MIME type from a filename */
+       public static String guessMIMEType(String arg) {
+               int x = arg.lastIndexOf('.');
+               if(x == -1 || x == arg.length()-1)
+                       return DEFAULT_MIME_TYPE;
+               String ext = arg.substring(x+1).toLowerCase();
+               Short mimeIndexOb = mimeTypesByExtension.get(ext);
+               if(mimeIndexOb != null) {
+                       return mimeTypesByNumber.get(mimeIndexOb.intValue());
+               }
+               return DEFAULT_MIME_TYPE;
+       }
+
+       public static String getExtension(String type) {
+               short typeNumber = byName(type);
+               if(typeNumber < 0) return null;
+               return primaryExtensionByMimeNumber.get(typeNumber);
+       }
+       
+       public static String[] getAllMIMETypes() {
+               return mimeTypesByNumber.toArray(new String[mimeTypesByNumber.size()]);
+       }
+
+}
diff --git a/src/de/todesbaum/util/swing/SortedListModel.java b/src/de/todesbaum/util/swing/SortedListModel.java
new file mode 100644 (file)
index 0000000..9d341dd
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.swing;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.swing.AbstractListModel;
+
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: SortedListModel.java 338 2006-03-20 15:40:48Z bombe $
+ */
+public class SortedListModel extends AbstractListModel implements List {
+       
+       private List elements = new ArrayList();
+       
+       /**
+        * {@inheritDoc}
+        */
+       public int getSize() {
+               return size();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object getElementAt(int index) {
+               return elements.get(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void add(int index, Object element) {
+               elements.add(index, element);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean add(Object o) {
+               boolean result = elements.add(o);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean addAll(Collection c) {
+               boolean result = elements.addAll(c);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean addAll(int index, Collection c) {
+               boolean result = elements.addAll(index, c);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void clear() {
+               elements.clear();
+               fireContentsChanged(this, 0, size());
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean contains(Object o) {
+               return elements.contains(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean containsAll(Collection c) {
+               return elements.containsAll(c);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean equals(Object o) {
+               return elements.equals(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object get(int index) {
+               return elements.get(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int hashCode() {
+               return elements.hashCode();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int indexOf(Object o) {
+               return elements.indexOf(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean isEmpty() {
+               return elements.isEmpty();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Iterator iterator() {
+               return elements.iterator();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int lastIndexOf(Object o) {
+               return elements.lastIndexOf(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public ListIterator listIterator() {
+               return elements.listIterator();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public ListIterator listIterator(int index) {
+               return elements.listIterator(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object remove(int index) {
+               fireContentsChanged(this, 0, size());
+               return elements.remove(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean remove(Object o) {
+               fireContentsChanged(this, 0, size());
+               return elements.remove(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean removeAll(Collection c) {
+               fireContentsChanged(this, 0, size());
+               return elements.removeAll(c);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean retainAll(Collection c) {
+               fireContentsChanged(this, 0, size());
+               return elements.retainAll(c);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object set(int index, Object element) {
+               Object result = elements.set(index, element);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int size() {
+               return elements.size();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public List subList(int fromIndex, int toIndex) {
+               return elements.subList(fromIndex, toIndex);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object[] toArray() {
+               return elements.toArray();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object[] toArray(Object[] a) {
+               return elements.toArray(a);
+       }
+
+}
diff --git a/src/de/todesbaum/util/swing/TLabel.java b/src/de/todesbaum/util/swing/TLabel.java
new file mode 100644 (file)
index 0000000..617f1d7
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.swing;
+
+import java.awt.Component;
+
+import javax.swing.Icon;
+import javax.swing.JLabel;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: TLabel.java 280 2006-03-17 20:24:27Z bombe $
+ */
+public class TLabel extends JLabel {
+
+       public TLabel() {
+               super();
+       }
+
+       public TLabel(int mnemonic, Component labelFor) {
+               super();
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+       }
+
+       public TLabel(Icon image) {
+               super(image);
+       }
+       
+       public TLabel(Icon image, int mnemonic, Component labelFor) {
+               super(image);
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+       }
+
+       public TLabel(Icon image, int horizontalAlignment) {
+               super(image);
+       }
+
+       public TLabel(Icon image, int horizontalAlignment, int mnemonic, Component labelFor) {
+               super(image);
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+       }
+
+       public TLabel(String text) {
+               super(text);
+       }
+
+       public TLabel(String text, int mnemonic, Component labelFor) {
+               super(text);
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+               setAlignmentX(0.0f);
+       }
+
+       public TLabel(String text, Icon icon, int horizontalAlignment) {
+               super(text, icon, horizontalAlignment);
+       }
+
+       public TLabel(String text, Icon icon, int horizontalAlignment, int mnemonic, Component labelFor) {
+               super(text, icon, horizontalAlignment);
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+       }
+
+       public TLabel(String text, int horizontalAlignment) {
+               super(text, horizontalAlignment);
+       }
+
+       public TLabel(String text, int horizontalAlignment, int mnemonic, Component labelFor) {
+               super(text, horizontalAlignment);
+               setDisplayedMnemonic(mnemonic);
+               setLabelFor(labelFor);
+       }
+
+}
diff --git a/src/de/todesbaum/util/swing/TWizard.java b/src/de/todesbaum/util/swing/TWizard.java
new file mode 100644 (file)
index 0000000..234f969
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: TWizard.java 426 2006-03-29 18:02:50Z bombe $
+ */
+public class TWizard extends JFrame implements WindowListener {
+       
+       protected List<WizardListener> wizardListeners = new ArrayList<WizardListener>();
+       
+       private Action previousAction;
+       private Action nextAction;
+       private Action quitAction;
+       private JLabel pageIcon;
+       private JPanel pagePanel;
+       private JLabel pageHeading;
+       private JLabel pageDescription;
+       
+       protected void frameInit() {
+               super.frameInit();
+               setResizable(false);
+               addWindowListener(this);
+               setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+               createActions();
+               
+               pageIcon = new JLabel();
+               pageIcon.setVerticalAlignment(SwingConstants.TOP);
+               pageHeading = new JLabel();
+               pageHeading.setFont(pageHeading.getFont().deriveFont(pageHeading.getFont().getSize() * 2.0f).deriveFont(Font.BOLD));
+               pageDescription = new JLabel();
+               
+               JPanel contentPane = new JPanel(new BorderLayout(12, 12));
+               contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));
+               
+               JPanel topPanel = new JPanel(new BorderLayout(12, 12));
+               contentPane.add(topPanel, BorderLayout.PAGE_START);
+               
+               topPanel.add(pageIcon, BorderLayout.LINE_START);
+               
+               JPanel textPanel = new JPanel(new BorderLayout(12, 12));
+               topPanel.add(textPanel, BorderLayout.CENTER);
+               textPanel.add(pageHeading, BorderLayout.PAGE_START);
+               textPanel.add(pageDescription, BorderLayout.CENTER);
+               
+               pagePanel = new JPanel(new BorderLayout(12, 12));
+               contentPane.add(pagePanel, BorderLayout.CENTER);
+               
+               JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
+               buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12));
+               buttonPanel.add(new JButton(previousAction));
+               buttonPanel.add(new JButton(nextAction));
+               buttonPanel.add(new JButton(quitAction));
+               contentPane.add(buttonPanel, BorderLayout.PAGE_END);
+               
+               setContentPane(contentPane);
+       }
+       
+       @Override
+       public void pack() {
+               super.pack();
+               Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+               setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2);
+               // System.out.println("resized to: " + getWidth() + "x" + getHeight());
+       }
+       
+       private void createActions() {
+               previousAction = new AbstractAction("Previous") {
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionPrevious();
+                       }
+               };
+               
+               nextAction = new AbstractAction("Next") {
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionNext();
+                       }
+               };
+               
+               quitAction = new AbstractAction("Quit") {
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionQuit();
+                       }
+               };
+       }
+       
+       public void addWizardListener(WizardListener wizardListener) {
+               wizardListeners.add(wizardListener);
+       }
+       
+       public void removeWizardListener(WizardListener wizardListener) {
+               wizardListeners.remove(wizardListener);
+       }
+       
+       protected void fireWizardPreviousPressed() {
+               for (WizardListener wizardListener: wizardListeners) {
+                       wizardListener.wizardPreviousPressed(this);
+               }
+       }
+       
+       protected void fireWizardNextPressed() {
+               for (WizardListener wizardListener: wizardListeners) {
+                       wizardListener.wizardNextPressed(this);
+               }
+       }
+       
+       protected void fireWizardQuitPressed() {
+               for (WizardListener wizardListener: wizardListeners) {
+                       wizardListener.wizardQuitPressed(this);
+               }
+       }
+       
+       public void setIcon(Icon icon) {
+               pageIcon.setIcon(icon);
+       }
+       
+       public void setPage(TWizardPage page) {
+               setVisible(false);
+               pageHeading.setText(page.getHeading());
+               pageDescription.setText(page.getDescription());
+               if (pagePanel.getComponentCount() > 0) {
+                       if (pagePanel.getComponent(0) instanceof TWizardPage) {
+                               ((TWizardPage) pagePanel.getComponent(0)).pageDeleted(this);
+                       }
+               }
+               pagePanel.removeAll();
+               pagePanel.add(page, BorderLayout.CENTER);
+               page.pageAdded(this);
+               pack();
+               setTitle(page.getHeading());
+               setVisible(true);
+       }
+       
+       public TWizardPage getPage() {
+               return (TWizardPage) pagePanel.getComponent(0);
+       }
+       
+       public void setPreviousEnabled(boolean previousEnabled) {
+               previousAction.setEnabled(previousEnabled);
+       }
+       
+       public void setPreviousName(String previousName) {
+               previousAction.putValue(Action.NAME, previousName);
+       }
+       
+       public void setNextEnabled(boolean nextEnabled) {
+               nextAction.setEnabled(nextEnabled);
+       }
+       
+       public void setNextName(String nextName) {
+               nextAction.putValue(Action.NAME, nextName);
+       }
+       
+       public void setQuitEnabled(boolean quitEnabled) {
+               quitAction.setEnabled(quitEnabled);
+       }
+       
+       public void setQuitName(String quitName) {
+               quitAction.putValue(Action.NAME, quitName);
+       }
+       
+       protected void actionPrevious() {
+               fireWizardPreviousPressed();
+       }
+
+       protected void actionNext() {
+               fireWizardNextPressed();
+       }
+       
+       protected void actionQuit() {
+               fireWizardQuitPressed();
+       }
+       
+       //
+       // INTERFACE WindowListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowOpened(WindowEvent e) {
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowClosing(WindowEvent e) {
+               fireWizardQuitPressed();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowClosed(WindowEvent e) {
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowIconified(WindowEvent e) {
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowDeiconified(WindowEvent e) {
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowActivated(WindowEvent e) {
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void windowDeactivated(WindowEvent e) {
+       }
+
+}
diff --git a/src/de/todesbaum/util/swing/TWizardPage.java b/src/de/todesbaum/util/swing/TWizardPage.java
new file mode 100644 (file)
index 0000000..ec5942e
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.swing;
+
+import javax.swing.JPanel;
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: TWizardPage.java 427 2006-03-29 18:03:06Z bombe $
+ */
+public class TWizardPage extends JPanel {
+
+       protected String heading;
+       protected String description;
+
+       public TWizardPage() {
+       }
+
+       public TWizardPage(String heading) {
+               this.heading = heading;
+       }
+
+       public TWizardPage(String heading, String description) {
+               this(heading);
+               this.description = description;
+       }
+
+       /**
+        * @return Returns the description.
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * @param description
+        *            The description to set.
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+       /**
+        * @return Returns the heading.
+        */
+       public String getHeading() {
+               return heading;
+       }
+
+       /**
+        * @param heading
+        *            The heading to set.
+        */
+       public void setHeading(String heading) {
+               this.heading = heading;
+       }
+
+       public void pageAdded(TWizard wizard) {
+       }
+
+       public void pageDeleted(TWizard wizard) {
+       }
+
+}
diff --git a/src/de/todesbaum/util/swing/WizardListener.java b/src/de/todesbaum/util/swing/WizardListener.java
new file mode 100644 (file)
index 0000000..b4328fc
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * todesbaum-lib - 
+ * Copyright (C) 2006 David Roden
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.swing;
+
+import java.util.EventListener;
+
+
+/**
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id: WizardListener.java 280 2006-03-17 20:24:27Z bombe $
+ */
+public interface WizardListener extends EventListener {
+
+       public void wizardNextPressed(TWizard wizard);
+       public void wizardPreviousPressed(TWizard wizard);
+       public void wizardQuitPressed(TWizard wizard);
+       
+}
diff --git a/src/de/todesbaum/util/xml/SimpleXML.java b/src/de/todesbaum/util/xml/SimpleXML.java
new file mode 100644 (file)
index 0000000..d400ed8
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * SimpleXML is a helper class to construct XML trees in a fast and simple way. Construct a new XML tree by calling {@link #SimpleXML(String)} and
+ * append new nodes by calling {@link #append(String)}.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id:SimpleXML.java 221 2006-03-06 14:46:49Z bombe $
+ */
+public class SimpleXML {
+
+       /**
+        * A {@link List} containing all child nodes of this node.
+        */
+       private List<SimpleXML> children = new ArrayList<SimpleXML>();
+
+       /**
+        * The name of this node.
+        */
+       private String name = null;
+
+       /**
+        * The value of this node.
+        */
+       private String value = null;
+
+       /**
+        * Constructs a new XML node without a name.
+        */
+       public SimpleXML() {
+               super();
+       }
+
+       /**
+        * Constructs a new XML node with the specified name.
+        * 
+        * @param name
+        *            The name of the new node
+        */
+       public SimpleXML(String name) {
+               this.name = name;
+       }
+
+       /**
+        * Returns the child node of this node with the specified name. If there are several child nodes with the specified name only the first node is
+        * returned.
+        * 
+        * @param nodeName
+        *            The name of the child node
+        * @return The child node, or <code>null</code> if there is no child node with the specified name
+        */
+       public SimpleXML getNode(String nodeName) {
+               for (int index = 0, count = children.size(); index < count; index++) {
+                       if (children.get(index).name.equals(nodeName)) {
+                               return children.get(index);
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the child node that is specified by the names. The first element of <code>nodeNames</code> is the name of the child node of this
+        * node, the second element of <code>nodeNames</code> is the name of a child node's child node, and so on. By using this method you can descend
+        * into an XML tree pretty fast.
+        * 
+        * <pre>
+        * SimpleXML deepNode = topNode.getNodes(new String[] { &quot;person&quot;, &quot;address&quot;, &quot;number&quot; });
+        * </pre>
+        * 
+        * @param nodeNames
+        * @return A node that is a deep child of this node, or <code>null</code> if the specified node does not eixst
+        */
+       public SimpleXML getNode(String[] nodeNames) {
+               SimpleXML node = this;
+               for (String nodeName: nodeNames) {
+                       node = node.getNode(nodeName);
+               }
+               return node;
+       }
+
+       /**
+        * Returns all child nodes of this node.
+        * 
+        * @return All child nodes of this node
+        */
+       public SimpleXML[] getNodes() {
+               return getNodes(null);
+       }
+
+       /**
+        * Returns all child nodes of this node with the specified name. If there are no child nodes with the specified name an empty array is returned.
+        * 
+        * @param nodeName
+        *            The name of the nodes to retrieve, or <code>null</code> to retrieve all nodes
+        * @return All child nodes with the specified name
+        */
+       public SimpleXML[] getNodes(String nodeName) {
+               List<SimpleXML> resultList = new ArrayList<SimpleXML>();
+               for (SimpleXML child: children) {
+                       if ((nodeName == null) || child.name.equals(nodeName)) {
+                               resultList.add(child);
+                       }
+               }
+               return resultList.toArray(new SimpleXML[resultList.size()]);
+       }
+
+       /**
+        * Appends a new XML node with the specified name and returns the new node. With this method you can create deep structures very fast.
+        * 
+        * <pre>
+        * SimpleXML mouseNode = topNode.append(&quot;computer&quot;).append(&quot;bus&quot;).append(&quot;usb&quot;).append(&quot;mouse&quot;);
+        * </pre>
+        * 
+        * @param nodeName
+        *            The name of the node to append as a child to this node
+        * @return The new node
+        */
+       public SimpleXML append(String nodeName) {
+               return append(new SimpleXML(nodeName));
+       }
+
+       /**
+        * Appends a new XML node with the specified name and value and returns the new node.
+        * 
+        * @param nodeName
+        *            The name of the node to append
+        * @param nodeValue
+        *            The value of the node to append
+        * @return The newly appended node
+        */
+       public SimpleXML append(String nodeName, String nodeValue) {
+               return append(nodeName).setValue(nodeValue);
+       }
+
+       /**
+        * Appends the node with all its child nodes to this node and returns the child node.
+        * 
+        * @param newChild
+        *            The node to append as a child
+        * @return The child node that was appended
+        */
+       public SimpleXML append(SimpleXML newChild) {
+               children.add(newChild);
+               return newChild;
+       }
+
+       public void remove(SimpleXML child) {
+               children.remove(child);
+       }
+       
+       public void remove(String childName) {
+               SimpleXML child = getNode(childName);
+               if (child != null) {
+                       remove(child);
+               }
+       }
+       
+       public void replace(String childName, String value) {
+               remove(childName);
+               append(childName, value);
+       }
+       
+       public void replace(SimpleXML childNode) {
+               remove(childNode.getName());
+               append(childNode);
+       }
+       
+       public void removeAll() {
+               children.clear();
+       }
+
+       /**
+        * Sets the value of this node.
+        * 
+        * @param nodeValue
+        *            The new value of this node
+        * @return This node
+        */
+       public SimpleXML setValue(String nodeValue) {
+               value = nodeValue;
+               return this;
+       }
+
+       /**
+        * Returns the name of this node.
+        * 
+        * @return The name of this node
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Returns the value of this node.
+        * 
+        * @return The value of this node
+        */
+       public String getValue() {
+               return value;
+       }
+
+       /**
+        * Creates a {@link Document} from this node and all its child nodes.
+        * 
+        * @return The {@link Document} created from this node
+        */
+       public Document getDocument() {
+               DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+               try {
+                       DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+                       Document document = documentBuilder.newDocument();
+                       Element rootElement = document.createElement(name);
+                       document.appendChild(rootElement);
+                       addChildren(rootElement);
+                       return document;
+               } catch (ParserConfigurationException e) {
+               }
+               return null;
+       }
+
+       /**
+        * Appends all children of this node to the specified {@link Element}. If a node has a value that is not <code>null</code> the value is
+        * appended as a text node.
+        * 
+        * @param rootElement
+        *            The element to attach this node's children to
+        */
+       private void addChildren(Element rootElement) {
+               for (SimpleXML child: children) {
+                       Element childElement = rootElement.getOwnerDocument().createElement(child.name);
+                       rootElement.appendChild(childElement);
+                       if (child.value != null) {
+                               Text childText = rootElement.getOwnerDocument().createTextNode(child.value);
+                               childElement.appendChild(childText);
+                       } else {
+                               child.addChildren(childElement);
+                       }
+               }
+       }
+
+       /**
+        * Creates a SimpleXML node from the specified {@link Document}. The SimpleXML node of the document's top-level node is returned.
+        * 
+        * @param document
+        *            The {@link Document} to create a SimpleXML node from
+        * @return The SimpleXML node created from the document's top-level node
+        */
+       public static SimpleXML fromDocument(Document document) {
+               SimpleXML xmlDocument = new SimpleXML(document.getFirstChild().getNodeName());
+               document.normalizeDocument();
+               return addDocumentChildren(xmlDocument, document.getFirstChild());
+       }
+
+       /**
+        * Appends the child nodes of the specified {@link Document} to this node. Text nodes are converted into a node's value.
+        * 
+        * @param xmlDocument
+        *            The SimpleXML node to append the child nodes to
+        * @param document
+        *            The document whose child nodes to append
+        * @return The SimpleXML node the child nodes were appended to
+        */
+       private static SimpleXML addDocumentChildren(SimpleXML xmlDocument, Node document) {
+               NodeList childNodes = document.getChildNodes();
+               for (int childIndex = 0, childCount = childNodes.getLength(); childIndex < childCount; childIndex++) {
+                       Node childNode = childNodes.item(childIndex);
+                       if ((childNode.getChildNodes().getLength() == 1) && (childNode.getFirstChild().getNodeName().equals("#text")) /*&& (childNode.getFirstChild().getNodeValue().trim().length() != 0)*/) {
+                               xmlDocument.append(childNode.getNodeName(), childNode.getFirstChild().getNodeValue());
+                       } else {
+                               if (!childNode.getNodeName().equals("#text") || (childNode.getChildNodes().getLength() != 0)) {
+                                       SimpleXML newXML = xmlDocument.append(childNode.getNodeName());
+                                       addDocumentChildren(newXML, childNode);
+                               }
+                       }
+               }
+               return xmlDocument;
+       }
+
+}
diff --git a/src/de/todesbaum/util/xml/XML.java b/src/de/todesbaum/util/xml/XML.java
new file mode 100644 (file)
index 0000000..d84fd75
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package de.todesbaum.util.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * Contains method to transform DOM XML trees to byte arrays and vice versa.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id:XML.java 221 2006-03-06 14:46:49Z bombe $
+ */
+public class XML {
+
+       /** Cached document builder factory. */
+       private static DocumentBuilderFactory documentBuilderFactory = null;
+
+       /** Cached document builder. */
+       private static DocumentBuilder documentBuilder = null;
+
+       /** Cached transformer factory. */
+       private static TransformerFactory transformerFactory = null;
+
+       /** Does nothing. */
+       private XML() {
+       }
+
+       /**
+        * Returns a document builder factory. If possible the cached instance will be returned.
+        * 
+        * @return A document builder factory
+        */
+       private static DocumentBuilderFactory getDocumentBuilderFactory() {
+               if (documentBuilderFactory != null) {
+                       return documentBuilderFactory;
+               }
+               documentBuilderFactory = DocumentBuilderFactory.newInstance();
+               return documentBuilderFactory;
+       }
+
+       /**
+        * Returns a document builder. If possible the cached instance will be returned.
+        * 
+        * @return A document builder
+        */
+       private static DocumentBuilder getDocumentBuilder() {
+               if (documentBuilder != null) {
+                       return documentBuilder;
+               }
+               try {
+                       documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
+               } catch (ParserConfigurationException e) {
+               }
+               return documentBuilder;
+       }
+
+       /**
+        * Returns a transformer factory. If possible the cached instance will be returned.
+        * 
+        * @return A transformer factory
+        */
+       private static TransformerFactory getTransformerFactory() {
+               if (transformerFactory != null) {
+                       return transformerFactory;
+               }
+               transformerFactory = TransformerFactory.newInstance();
+               return transformerFactory;
+       }
+
+       /**
+        * Creates a new XML document.
+        * 
+        * @return A new XML document
+        */
+       public static Document createDocument() {
+               return getDocumentBuilder().newDocument();
+       }
+
+       /**
+        * Transforms the DOM XML document into a byte array.
+        * 
+        * @param document
+        *            The document to transform
+        * @return The byte array containing the XML representation
+        */
+       public static byte[] transformToByteArray(Document document) {
+               ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
+               OutputStreamWriter converter = new OutputStreamWriter(byteOutput, Charset.forName("UTF-8"));
+               Result transformResult = new StreamResult(converter);
+               Source documentSource = new DOMSource(document);
+               try {
+                       Transformer transformer = getTransformerFactory().newTransformer();
+                       transformer.transform(documentSource, transformResult);
+                       byteOutput.close();
+                       return byteOutput.toByteArray();
+               } catch (IOException ioe1) {
+               } catch (TransformerConfigurationException tce1) {
+               } catch (TransformerException te1) {
+               } finally {
+                       try {
+                               byteOutput.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Transforms the byte array into a DOM XML document.
+        * 
+        * @param data
+        *            The byte array to parse
+        * @return The DOM XML document
+        */
+       public static Document transformToDocument(byte[] data) {
+               ByteArrayInputStream byteInput = new ByteArrayInputStream(data);
+               InputStreamReader converter = new InputStreamReader(byteInput, Charset.forName("UTF-8"));
+               Source xmlSource = new StreamSource(converter);
+               Result xmlResult = new DOMResult();
+               try {
+                       Transformer transformer = getTransformerFactory().newTransformer();
+                       transformer.transform(xmlSource, xmlResult);
+                       return (Document) ((DOMResult) xmlResult).getNode();
+               } catch (TransformerConfigurationException tce1) {
+               } catch (TransformerException te1) {
+               } finally {
+                       if (byteInput != null)
+                               try {
+                                       byteInput.close();
+                               } catch (IOException ioe1) {
+                               }
+                       if (converter != null)
+                               try {
+                                       converter.close();
+                               } catch (IOException ioe1) {
+                               }
+               }
+               return null;
+       }
+
+}