Mavenize jSite.
authorDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Sun, 26 Aug 2012 16:57:20 +0000 (18:57 +0200)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Sun, 26 Aug 2012 16:57:20 +0000 (18:57 +0200)
135 files changed:
.classpath
.project
images/flag-de.png [deleted file]
images/flag-en.png [deleted file]
images/flag-fr.png [deleted file]
images/jsite-icon.png [deleted file]
pom.xml [new file with mode: 0644]
src/de/todesbaum/jsite/application/AbortedException.java [deleted file]
src/de/todesbaum/jsite/application/FileOption.java [deleted file]
src/de/todesbaum/jsite/application/Freenet7Interface.java [deleted file]
src/de/todesbaum/jsite/application/InsertListener.java [deleted file]
src/de/todesbaum/jsite/application/KeyDialog.java [deleted file]
src/de/todesbaum/jsite/application/Node.java [deleted file]
src/de/todesbaum/jsite/application/Project.java [deleted file]
src/de/todesbaum/jsite/application/ProjectInserter.java [deleted file]
src/de/todesbaum/jsite/application/UpdateChecker.java [deleted file]
src/de/todesbaum/jsite/application/UpdateListener.java [deleted file]
src/de/todesbaum/jsite/gui/FileScanner.java [deleted file]
src/de/todesbaum/jsite/gui/FileScannerListener.java [deleted file]
src/de/todesbaum/jsite/gui/NodeManagerListener.java [deleted file]
src/de/todesbaum/jsite/gui/NodeManagerPage.java [deleted file]
src/de/todesbaum/jsite/gui/PreferencesPage.java [deleted file]
src/de/todesbaum/jsite/gui/ProjectFilesPage.java [deleted file]
src/de/todesbaum/jsite/gui/ProjectInsertPage.java [deleted file]
src/de/todesbaum/jsite/gui/ProjectPage.java [deleted file]
src/de/todesbaum/jsite/i18n/I18n.java [deleted file]
src/de/todesbaum/jsite/i18n/I18nContainer.java [deleted file]
src/de/todesbaum/jsite/i18n/jSite.properties [deleted file]
src/de/todesbaum/jsite/i18n/jSite_de.properties [deleted file]
src/de/todesbaum/jsite/i18n/jSite_fr.properties [deleted file]
src/de/todesbaum/jsite/main/CLI.java [deleted file]
src/de/todesbaum/jsite/main/Configuration.java [deleted file]
src/de/todesbaum/jsite/main/ConfigurationLocator.java [deleted file]
src/de/todesbaum/jsite/main/Main.java [deleted file]
src/de/todesbaum/jsite/main/Version.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Client.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ClientGet.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ClientHello.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ClientPut.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Command.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Connection.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/FileEntry.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Message.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Node.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Persistence.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/PriorityClass.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/ReturnType.java [deleted file]
src/de/todesbaum/util/freenet/fcp2/Verbosity.java [deleted file]
src/de/todesbaum/util/image/IconLoader.java [deleted file]
src/de/todesbaum/util/io/Closer.java [deleted file]
src/de/todesbaum/util/io/LineInputStream.java [deleted file]
src/de/todesbaum/util/io/StreamCopier.java [deleted file]
src/de/todesbaum/util/io/TeeOutputStream.java [deleted file]
src/de/todesbaum/util/io/TempFileInputStream.java [deleted file]
src/de/todesbaum/util/mime/DefaultMIMETypes.java [deleted file]
src/de/todesbaum/util/swing/SortedListModel.java [deleted file]
src/de/todesbaum/util/swing/TLabel.java [deleted file]
src/de/todesbaum/util/swing/TWizard.java [deleted file]
src/de/todesbaum/util/swing/TWizardPage.java [deleted file]
src/de/todesbaum/util/swing/WizardListener.java [deleted file]
src/de/todesbaum/util/xml/SimpleXML.java [deleted file]
src/de/todesbaum/util/xml/XML.java [deleted file]
src/main/java/de/todesbaum/jsite/application/AbortedException.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/FileOption.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/InsertListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/KeyDialog.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/Node.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/Project.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/ProjectInserter.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/UpdateChecker.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/application/UpdateListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/FileScanner.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/gui/ProjectPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/i18n/I18n.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/CLI.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/Configuration.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/ConfigurationLocator.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/Main.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/Version.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Client.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Command.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Connection.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Message.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Node.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/image/IconLoader.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/io/Closer.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/io/LineInputStream.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/io/StreamCopier.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/io/TeeOutputStream.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/io/TempFileInputStream.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/swing/SortedListModel.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/swing/TLabel.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/swing/TWizard.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/swing/TWizardPage.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/swing/WizardListener.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/xml/SimpleXML.java [new file with mode: 0644]
src/main/java/de/todesbaum/util/xml/XML.java [new file with mode: 0644]
src/main/resources/de/todesbaum/jsite/i18n/jSite.properties [new file with mode: 0644]
src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties [new file with mode: 0644]
src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties [new file with mode: 0644]
src/main/resources/flag-de.png [new file with mode: 0644]
src/main/resources/flag-en.png [new file with mode: 0644]
src/main/resources/flag-fr.png [new file with mode: 0644]
src/main/resources/jsite-icon.png [new file with mode: 0644]

index 98130cb..683f2af 100644 (file)
@@ -1,7 +1,21 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="images"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-       <classpathentry kind="output" path="bin"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/java">
+               <attributes>
+                       <attribute name="optional" value="true"/>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" path="src/main/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+               <attributes>
+                       <attribute name="maven.pomderived" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="target/classes"/>
 </classpath>
index c84cfc3..05eafae 100644 (file)
--- a/.project
+++ b/.project
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
                <nature>org.eclipse.jdt.core.javanature</nature>
        </natures>
 </projectDescription>
diff --git a/images/flag-de.png b/images/flag-de.png
deleted file mode 100644 (file)
index d1b572b..0000000
Binary files a/images/flag-de.png and /dev/null differ
diff --git a/images/flag-en.png b/images/flag-en.png
deleted file mode 100644 (file)
index 33472a7..0000000
Binary files a/images/flag-en.png and /dev/null differ
diff --git a/images/flag-fr.png b/images/flag-fr.png
deleted file mode 100644 (file)
index 5793e70..0000000
Binary files a/images/flag-fr.png and /dev/null differ
diff --git a/images/jsite-icon.png b/images/jsite-icon.png
deleted file mode 100644 (file)
index fcddf6c..0000000
Binary files a/images/jsite-icon.png and /dev/null differ
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..732101c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,80 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <groupId>net.pterodactylus</groupId>
+       <artifactId>jSite</artifactId>
+       <version>0.11-SNAPSHOT</version>
+       <repositories>
+               <repository>
+                       <id>pterodactylus</id>
+                       <url>http://maven.pterodactylus.net/</url>
+               </repository>
+       </repositories>
+       <properties>
+               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+       </properties>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                               <configuration>
+                                       <source>1.6</source>
+                                       <target>1.6</target>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-jar-plugin</artifactId>
+                               <configuration>
+                                       <archive>
+                                               <manifest>
+                                                       <mainClass>de.todesbaum.jsite.main.Main</mainClass>
+                                               </manifest>
+                                       </archive>
+                               </configuration>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-assembly-plugin</artifactId>
+                               <version>2.2-beta-5</version>
+                               <configuration>
+                                       <descriptorRefs>
+                                               <descriptorRef>jar-with-dependencies</descriptorRef>
+                                       </descriptorRefs>
+                                       <archiverConfig>
+                                               <duplicateBehavior>skip</duplicateBehavior>
+                                       </archiverConfig>
+                                       <archive>
+                                               <manifest>
+                                                       <mainClass>de.todesbaum.jsite.main.Main</mainClass>
+                                               </manifest>
+                                       </archive>
+                               </configuration>
+                               <executions>
+                                       <execution>
+                                               <id>make-assembly</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>single</goal>
+                                               </goals>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <reporting>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-javadoc-plugin</artifactId>
+                               <configuration>
+                                       <links>
+                                               <link>http://download.oracle.com/javase/6/docs/api/</link>
+                                               <link>http://java.pterodactylus.net/utils/apidocs/</link>
+                                               <link>http://java.pterodactylus.net/utils.json/apidocs/</link>
+                                       </links>
+                               </configuration>
+                       </plugin>
+               </plugins>
+       </reporting>
+</project>
diff --git a/src/de/todesbaum/jsite/application/AbortedException.java b/src/de/todesbaum/jsite/application/AbortedException.java
deleted file mode 100644 (file)
index 0dfd8c7..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * jSite - AbortedException.java - Copyright © 2010–2012 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;
-
-/**
- * Marker exception that signals that the user aborted an insert.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class AbortedException extends Exception {
-
-       /* nothing here. */
-
-}
diff --git a/src/de/todesbaum/jsite/application/FileOption.java b/src/de/todesbaum/jsite/application/FileOption.java
deleted file mode 100644 (file)
index c8824de..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * jSite - FileOption.java - Copyright © 2006–2012 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;
-
-/**
- * Container for various file options.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class FileOption {
-
-       /** The default for the insert state. */
-       private static final boolean DEFAULT_INSERT = true;
-
-       /** The default for the insert redirect state. */
-       private static final boolean DEFAULT_INSERT_REDIRECT = true;
-
-       /** The default for the custom key. */
-       private static final String DEFAULT_CUSTOM_KEY = "CHK@";
-
-       /** The default changed name. */
-       private static final String DEFAULT_CHANGED_NAME = null;
-
-       /** The insert state. */
-       private boolean insert;
-
-       /** Whether to force an insert. */
-       private boolean forceInsert;
-
-       /** Whether to insert a redirect. */
-       private boolean insertRedirect;
-
-       /** The hash of the last insert. */
-       private String lastInsertHash;
-
-       /** The edition of the last insert. */
-       private int lastInsertEdition;
-
-       /** The filename of the last insert. */
-       private String lastInsertFilename;
-
-       /** The current hash of the file. */
-       private String currentHash;
-
-       /** The custom key. */
-       private String customKey;
-
-       /** The changed name. */
-       private String changedName;
-
-       /** The default MIME type. */
-       private final String defaultMimeType;
-
-       /** The current MIME type. */
-       private String mimeType;
-
-       /**
-        * Creates new file options.
-        *
-        * @param defaultMimeType
-        *            The default MIME type of the file
-        */
-       public FileOption(String defaultMimeType) {
-               insert = DEFAULT_INSERT;
-               insertRedirect = DEFAULT_INSERT_REDIRECT;
-               customKey = DEFAULT_CUSTOM_KEY;
-               changedName = DEFAULT_CHANGED_NAME;
-               this.defaultMimeType = defaultMimeType;
-               mimeType = defaultMimeType;
-       }
-
-       /**
-        * Returns the custom key. The custom key is only used when
-        * {@link #isInsert()} and {@link #isInsertRedirect()} both return {@code
-        * true}.
-        *
-        * @return The custom key
-        */
-       public String getCustomKey() {
-               return customKey;
-       }
-
-       /**
-        * Sets the custom key. The custom key is only used when {@link #isInsert()}
-        * and {@link #isInsertRedirect()} both return {@code true}.
-        *
-        * @param customKey
-        *            The custom key
-        */
-       public void setCustomKey(String customKey) {
-               if (customKey == null) {
-                       this.customKey = "";
-               } else {
-                       this.customKey = customKey;
-               }
-       }
-
-       /**
-        * Returns whether the file should be inserted. If a file is not inserted
-        * and {@link #isInsertRedirect()} is also {@code false}, the file will not
-        * be inserted at all.
-        *
-        * @see #setCustomKey(String)
-        * @return <code>true</code> if the file should be inserted,
-        *         <code>false</code> otherwise
-        */
-       public boolean isInsert() {
-               return insert;
-       }
-
-       /**
-        * Sets whether the file should be inserted. If a file is not inserted and
-        * {@link #isInsertRedirect()} is also {@code false}, the file will not be
-        * inserted at all.
-        *
-        * @param insert
-        *            <code>true</code> if the file should be inserted,
-        *            <code>false</code> otherwise
-        */
-       public void setInsert(boolean insert) {
-               this.insert = insert;
-       }
-
-       /**
-        * Returns whether the insert of this file should be forced, even if its
-        * current hash matches the last insert hash.
-        *
-        * @return {@code true} to force the insert of this file, {@code false}
-        *         otherwise
-        */
-       public boolean isForceInsert() {
-               return forceInsert;
-       }
-
-       /**
-        * Sets whether to force the insert of this file, even if its current hash
-        * matches the last insert hash.
-        *
-        * @param forceInsert
-        *            {@code true} to force the insert of this file, {@code false}
-        *            otherwise
-        * @return These file options
-        */
-       public FileOption setForceInsert(boolean forceInsert) {
-               this.forceInsert = forceInsert;
-               return this;
-       }
-
-       /**
-        * Returns whether a redirect to a different key should be inserted. This
-        * will only matter if {@link #isInsert()} returns {@code false}. The key
-        * that should be redirected to still needs to be specified via
-        * {@link #setCustomKey(String)}.
-        *
-        * @return {@code true} if a redirect should be inserted, {@code false}
-        *         otherwise
-        */
-       public boolean isInsertRedirect() {
-               return insertRedirect;
-       }
-
-       /**
-        * Sets whether a redirect should be inserted. This will only matter if
-        * {@link #isInsert()} returns {@code false}, i.e. it has been
-        * {@link #setInsert(boolean)} to {@code false}. The key that should be
-        * redirected to still needs to be specified via
-        * {@link #setCustomKey(String)}.
-        *
-        * @param insertRedirect
-        *            {@code true} if a redirect should be inserted, {@code false}
-        *            otherwise
-        */
-       public void setInsertRedirect(boolean insertRedirect) {
-               this.insertRedirect = insertRedirect;
-       }
-
-       /**
-        * Returns the hash of the file when it was last inserted
-        *
-        * @return The last hash of the file
-        */
-       public String getLastInsertHash() {
-               return lastInsertHash;
-       }
-
-       /**
-        * Sets the hash of the file when it was last inserted.
-        *
-        * @param lastInsertHash
-        *            The last hash of the file
-        * @return These file options
-        */
-       public FileOption setLastInsertHash(String lastInsertHash) {
-               this.lastInsertHash = lastInsertHash;
-               return this;
-       }
-
-       /**
-        * Returns the last edition at which this file was inserted.
-        *
-        * @return The last insert edition of this file
-        */
-       public int getLastInsertEdition() {
-               return lastInsertEdition;
-       }
-
-       /**
-        * Sets the last insert edition of this file.
-        *
-        * @param lastInsertEdition
-        *            The last insert edition of this file
-        * @return These file options
-        */
-       public FileOption setLastInsertEdition(int lastInsertEdition) {
-               this.lastInsertEdition = lastInsertEdition;
-               return this;
-       }
-
-       /**
-        * Returns the name of the file when it was last inserted.
-        *
-        * @return The name of the file at the last insert
-        */
-       public String getLastInsertFilename() {
-               return lastInsertFilename;
-       }
-
-       /**
-        * Sets the name of the file when it was last inserted.
-        *
-        * @param lastInsertFilename
-        *            The name of the file at the last insert.
-        * @return These file options
-        */
-       public FileOption setLastInsertFilename(String lastInsertFilename) {
-               this.lastInsertFilename = lastInsertFilename;
-               return this;
-       }
-
-       /**
-        * Returns the current hash of the file. This value is ony a temporary value
-        * that is copied to {@link #getLastInsertHash()} when a project has
-        * finished inserting.
-        *
-        * @see Project#onSuccessfulInsert()
-        * @return The current hash of the file
-        */
-       public String getCurrentHash() {
-               return currentHash;
-       }
-
-       /**
-        * Sets the current hash of the file.
-        *
-        * @param currentHash
-        *            The current hash of the file
-        * @return These file options
-        */
-       public FileOption setCurrentHash(String currentHash) {
-               this.currentHash = currentHash;
-               return this;
-       }
-
-       /**
-        * Returns whether this file has a changed name. Use
-        * {@link #getChangedName()} is this method returns {@code true}.
-        *
-        * @return {@code true} if this file has a changed name, {@code false}
-        *         otherwise
-        */
-       public boolean hasChangedName() {
-               return (changedName != null) && (changedName.length() > 0);
-       }
-
-       /**
-        * Returns the changed name for this file. This method will return {@code
-        * null} or an empty {@link String} if this file should not be renamed.
-        *
-        * @return The changed name, or {@code null} if this file should not be
-        *         renamed
-        */
-       public String getChangedName() {
-               return changedName;
-       }
-
-       /**
-        * Sets the changed name for this file. Setting the changed file to {@code
-        * null} or an empty {@link String} will disable renaming.
-        *
-        * @param changedName
-        *            The new changed name for this file
-        */
-       public void setChangedName(String changedName) {
-               this.changedName = changedName;
-       }
-
-       /**
-        * Sets the MIME type of the file. Setting the MIME type to
-        * <code>null</code> will set the MIME type to the default MIME type.
-        *
-        * @param mimeType
-        *            The MIME type of the file
-        */
-       public void setMimeType(String mimeType) {
-               if (mimeType == null) {
-                       this.mimeType = defaultMimeType;
-               } else {
-                       this.mimeType = mimeType;
-               }
-       }
-
-       /**
-        * Returns the MIME type of the file. If no custom MIME type has been set,
-        * the default MIME type is returned.
-        *
-        * @return The MIME type of the file
-        */
-       public String getMimeType() {
-               return mimeType;
-       }
-
-       /**
-        * Returns whether the options for this file have been modified, i.e. are
-        * not at their default values.
-        *
-        * @return <code>true</code> if the options have been modified,
-        *         <code>false</code> if they are at default values
-        */
-       public boolean isCustom() {
-               if (insert != DEFAULT_INSERT) {
-                       return true;
-               }
-               if (!customKey.equals(DEFAULT_CUSTOM_KEY)) {
-                       return true;
-               }
-               if (((changedName != null) && !changedName.equals(DEFAULT_CHANGED_NAME)) || ((DEFAULT_CHANGED_NAME != null) && !DEFAULT_CHANGED_NAME.equals(changedName))) {
-                       return true;
-               }
-               if (!defaultMimeType.equals(mimeType)) {
-                       return true;
-               }
-               if (insertRedirect != DEFAULT_INSERT_REDIRECT) {
-                       return true;
-               }
-               return false;
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/application/Freenet7Interface.java b/src/de/todesbaum/jsite/application/Freenet7Interface.java
deleted file mode 100644 (file)
index 78250b2..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * jSite - Freenet7Interface.java - Copyright © 2006–2012 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;
-
-/**
- * Interface for freenet-related operations.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Freenet7Interface {
-
-       /** Random number to differentiate several jSites. */
-       private static final int number = (int) (Math.random() * Integer.MAX_VALUE);
-
-       /** Counter. */
-       private static int counter = 0;
-
-       /** The node to connect to. */
-       private Node node;
-
-       /** The connection to the node. */
-       private Connection connection;
-
-       /**
-        * Sets the hostname of the node. The default port for FCP2 connections ({@link Node#DEFAULT_PORT})
-        * is used.
-        *
-        * @param hostname
-        *            The hostname of the node
-        */
-       public void setNodeAddress(String hostname) {
-               node = new Node(hostname);
-               connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
-       }
-
-       /**
-        * Sets the hostname and the port of the node.
-        *
-        * @param hostname
-        *            The hostname of the node
-        * @param port
-        *            The port number of the node
-        */
-       public void setNodeAddress(String hostname, int port) {
-               node = new Node(hostname, port);
-               connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
-       }
-
-       /**
-        * Sets hostname and port from the given node.
-        *
-        * @param node
-        *            The node to get the hostname and port from
-        */
-       public void setNode(de.todesbaum.jsite.application.Node node) {
-               if (node != null) {
-                       this.node = new Node(node.getHostname(), node.getPort());
-                       connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
-               } else {
-                       this.node = null;
-                       connection = null;
-               }
-       }
-
-       /**
-        * Removes the current node from the interface.
-        */
-       public void removeNode() {
-               node = null;
-               connection = null;
-       }
-
-       /**
-        * Returns the node this interface is connecting to.
-        *
-        * @return The node
-        */
-       public Node getNode() {
-               return node;
-       }
-
-       /**
-        * Creates a new connection to the current node with the given identifier.
-        *
-        * @param identifier
-        *            The identifier of the connection
-        * @return The connection to the node
-        */
-       public Connection getConnection(String identifier) {
-               return new Connection(node, identifier);
-       }
-
-       /**
-        * Checks whether the current node is connected. If the node is not
-        * connected, a connection will be tried.
-        *
-        * @return <code>true</code> if the node is connected, <code>false</code>
-        *         otherwise
-        * @throws IOException
-        *             if an I/O error occurs communicating with the node
-        */
-       public boolean isNodePresent() throws IOException {
-               if (!connection.isConnected()) {
-                       return connection.connect();
-               }
-               return true;
-       }
-
-       /**
-        * Generates an SSK key pair.
-        *
-        * @return An array of strings, the first one being the generated private
-        *         (insert) URI and the second one being the generated public
-        *         (request) URI
-        * @throws IOException
-        *             if an I/O error occurs communicating with the node
-        */
-       public String[] generateKeyPair() throws IOException {
-               if (!isNodePresent()) {
-                       throw new IOException("Node is offline.");
-               }
-               GenerateSSK generateSSK = new GenerateSSK();
-               Client client = new Client(connection, generateSSK);
-               Message keypairMessage = client.readMessage();
-               return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") };
-       }
-
-       /**
-        * Checks whether the interface has already been configured with a node.
-        *
-        * @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
deleted file mode 100644 (file)
index 9b6b332..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * jSite - InsertListener.java - Copyright © 2006–2012 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;
-
-/**
- * Interface for objects that want to be notified abount insert events.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public interface InsertListener extends EventListener {
-
-       /**
-        * Enumeration for the different error situations.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       public static enum ErrorType {
-
-               /** The key does already exist. */
-               KEY_COLLISION,
-
-               /** The route to the key was not found. */
-               ROUTE_NOT_FOUND,
-
-               /** The data was not found. */
-               DATA_NOT_FOUND,
-
-               /** Error in the FCP communication. */
-               FCP_ERROR,
-
-               /** General error in the communication. */
-               IO_ERROR
-       }
-
-       /**
-        * Notifies a listener that an insert has started.
-        *
-        * @param project
-        *            The project that is now being inserted
-        */
-       public void projectInsertStarted(Project project);
-
-       /**
-        * Notifies a listener that the upload of a project has finished and the
-        * inserting will start now.
-        *
-        * @param project
-        *            The project that has been uploaded
-        */
-       public void projectUploadFinished(Project project);
-
-       /**
-        * Notifies a listener that a project insert has generated a URI.
-        *
-        * @param project
-        *            The project being inserted
-        * @param uri
-        *            The generated URI
-        */
-       public void projectURIGenerated(Project project, String uri);
-
-       /**
-        * Notifies a listener that an insert has made some progress.
-        *
-        * @param project
-        *            The project being inserted
-        * @param succeeded
-        *            The number of succeeded blocks
-        * @param failed
-        *            The number of failed blocks
-        * @param fatal
-        *            The number of fatally failed blocks
-        * @param total
-        *            The total number of blocks
-        * @param finalized
-        *            <code>true</code> if the total number of blocks has been
-        *            finalized, <code>false</code> otherwise
-        */
-       public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized);
-
-       /**
-        * Notifies a listener that a project insert has finished.
-        *
-        * @param project
-        *            The project being inserted
-        * @param success
-        *            <code>true</code> if the insert succeeded, <code>false</code>
-        *            otherwise
-        * @param cause
-        *            The cause of a failure, if any (may be <code>null</code>)
-        */
-       public void projectInsertFinished(Project project, boolean success, Throwable cause);
-
-}
diff --git a/src/de/todesbaum/jsite/application/KeyDialog.java b/src/de/todesbaum/jsite/application/KeyDialog.java
deleted file mode 100644 (file)
index 4b64f72..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * jSite - KeyDialog.java - Copyright © 2010–2012 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.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.Toolkit;
-import java.awt.event.ActionEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.IOException;
-import java.text.MessageFormat;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JSeparator;
-import javax.swing.JTextField;
-import javax.swing.KeyStroke;
-import javax.swing.SwingConstants;
-
-import de.todesbaum.jsite.i18n.I18n;
-import de.todesbaum.jsite.i18n.I18nContainer;
-
-/**
- * A dialog that lets the user edit the private and public key for a project.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class KeyDialog extends JDialog {
-
-       /** Interface to the freenet node. */
-       private final Freenet7Interface freenetInterface;
-
-       /** The public key. */
-       private String publicKey;
-
-       /** The private key. */
-       private String privateKey;
-
-       /** The “OK” button’s action. */
-       private Action okAction;
-
-       /** The “Cancel” button’s action. */
-       private Action cancelAction;
-
-       /** The “Regenerate” button’s action. */
-       private Action generateAction;
-
-       /** The text field for the private key. */
-       private JTextField privateKeyTextField;
-
-       /** The text field for the public key. */
-       private JTextField publicKeyTextField;
-
-       /** Whether the dialog was cancelled. */
-       private boolean cancelled;
-
-       /**
-        * Creates a new key dialog.
-        *
-        * @param freenetInterface
-        *            Interface to the freenet node
-        * @param parent
-        *            The parent frame
-        */
-       public KeyDialog(Freenet7Interface freenetInterface, JFrame parent) {
-               super(parent, I18n.getMessage("jsite.key-dialog.title"), true);
-               this.freenetInterface = freenetInterface;
-               addWindowListener(new WindowAdapter() {
-
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public void windowClosing(WindowEvent windowEvent) {
-                               actionCancel();
-                       }
-               });
-               initDialog();
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns whether the dialog was cancelled.
-        *
-        * @return {@code true} if the dialog was cancelled, {@code false} otherwise
-        */
-       public boolean wasCancelled() {
-               return cancelled;
-       }
-
-       /**
-        * Returns the public key.
-        *
-        * @return The public key
-        */
-       public String getPublicKey() {
-               return publicKey;
-       }
-
-       /**
-        * Sets the public key.
-        *
-        * @param publicKey
-        *            The public key
-        */
-       public void setPublicKey(String publicKey) {
-               this.publicKey = publicKey;
-               publicKeyTextField.setText(publicKey);
-               pack();
-       }
-
-       /**
-        * Returns the private key.
-        *
-        * @return The private key
-        */
-       public String getPrivateKey() {
-               return privateKey;
-       }
-
-       /**
-        * Sets the private key.
-        *
-        * @param privateKey
-        *            The private key
-        */
-       public void setPrivateKey(String privateKey) {
-               this.privateKey = privateKey;
-               privateKeyTextField.setText(privateKey);
-               pack();
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pack() {
-               super.pack();
-               Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-               setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2);
-       }
-
-       //
-       // PRIVATE METHODS
-       //
-
-       /**
-        * Creates all necessary actions.
-        */
-       private void createActions() {
-               okAction = new AbstractAction(I18n.getMessage("jsite.general.ok")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionOk();
-                       }
-               };
-               okAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.ok.tooltip"));
-               okAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ENTER);
-
-               cancelAction = new AbstractAction(I18n.getMessage("jsite.general.cancel")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionCancel();
-                       }
-               };
-               cancelAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.cancel.tooltip"));
-               cancelAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ESCAPE);
-
-               generateAction = new AbstractAction(I18n.getMessage("jsite.key-dialog.button.generate")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionGenerate();
-                       }
-               };
-               generateAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.generate.tooltip"));
-               generateAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK));
-       }
-
-       /**
-        * Initializes the dialog and all its components.
-        */
-       private void initDialog() {
-               createActions();
-               JPanel dialogPanel = new JPanel(new BorderLayout(12, 12));
-               dialogPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
-
-               JPanel contentPanel = new JPanel(new GridBagLayout());
-               dialogPanel.add(contentPanel, BorderLayout.CENTER);
-
-               final JLabel keysLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.keys"));
-               contentPanel.add(keysLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
-
-               final JLabel privateKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.private-key"));
-               contentPanel.add(privateKeyLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
-
-               privateKeyTextField = new JTextField();
-               contentPanel.add(privateKeyTextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0));
-
-               final JLabel publicKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.public-key"));
-               contentPanel.add(publicKeyLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-
-               publicKeyTextField = new JTextField();
-               contentPanel.add(publicKeyTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0));
-
-               final JLabel actionsLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.actions"));
-               contentPanel.add(actionsLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
-
-               JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
-               actionButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
-               contentPanel.add(actionButtonPanel, new GridBagConstraints(0, 4, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
-
-               actionButtonPanel.add(new JButton(generateAction));
-
-               JPanel separatorPanel = new JPanel(new BorderLayout(12, 12));
-               dialogPanel.add(separatorPanel, BorderLayout.PAGE_END);
-               separatorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.PAGE_START);
-
-               JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
-               buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
-               separatorPanel.add(buttonPanel, BorderLayout.CENTER);
-               buttonPanel.add(new JButton(okAction));
-               buttonPanel.add(new JButton(cancelAction));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               keysLabel.setText(I18n.getMessage("jsite.key-dialog.label.keys"));
-                               privateKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.private-key"));
-                               publicKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.public-key"));
-                               actionsLabel.setText(I18n.getMessage("jsite.key-dialog.label.actions"));
-                       }
-               });
-
-               getContentPane().add(dialogPanel, BorderLayout.CENTER);
-               pack();
-               setResizable(false);
-       }
-
-       //
-       // PRIVATE ACTIONS
-       //
-
-       /**
-        * Quits the dialog, accepting all changes.
-        */
-       private void actionOk() {
-               publicKey = publicKeyTextField.getText();
-               privateKey = privateKeyTextField.getText();
-               cancelled = false;
-               setVisible(false);
-       }
-
-       /**
-        * Quits the dialog, discarding all changes.
-        */
-       private void actionCancel() {
-               cancelled = true;
-               setVisible(false);
-       }
-
-       /**
-        * Generates a new key pair.
-        */
-       private void actionGenerate() {
-               if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.generate-new-key"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
-                       return;
-               }
-               String[] keyPair = null;
-               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;
-               }
-               publicKeyTextField.setText(keyPair[1].substring(keyPair[1].indexOf('@') + 1, keyPair[1].lastIndexOf('/')));
-               privateKeyTextField.setText(keyPair[0].substring(keyPair[0].indexOf('@') + 1, keyPair[0].lastIndexOf('/')));
-               pack();
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/application/Node.java b/src/de/todesbaum/jsite/application/Node.java
deleted file mode 100644 (file)
index df7492f..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * jSite - Node.java - Copyright © 2006–2012 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;
-
-/**
- * Container for node information.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Node extends de.todesbaum.util.freenet.fcp2.Node {
-
-       /** The name of the node. */
-       protected String name;
-
-       /**
-        * Creates a new node with the given hostname and the default port.
-        *
-        * @see de.todesbaum.util.freenet.fcp2.Node#DEFAULT_PORT
-        * @param hostname
-        *            The hostname of the new node
-        */
-       public Node(String hostname) {
-               this(hostname, DEFAULT_PORT);
-       }
-
-       /**
-        * Creates a new node with the given hostname and port.
-        *
-        * @param hostname
-        *            The hostname of the new node
-        * @param port
-        *            The port of the new node
-        */
-       public Node(String hostname, int port) {
-               this(hostname, port, "");
-       }
-
-       /**
-        * Creates a new node with the given hostname, port, and name.
-        *
-        * @param hostname
-        *            The hostname of the new node
-        * @param port
-        *            The port of the new node
-        * @param name
-        *            The name of the node
-        */
-       public Node(String hostname, int port, String name) {
-               super(hostname, port);
-               this.name = name;
-       }
-
-       /**
-        * Creates a new node that gets its settings from the given node.
-        *
-        * @param node
-        *            The node to copy
-        */
-       public Node(Node node) {
-               this(node.getHostname(), node.getPort());
-       }
-
-       /**
-        * Creates a new node from the given node, overwriting the name.
-        *
-        * @param node
-        *            The node to copy from
-        * @param name
-        *            The new name of the node
-        */
-       public Node(Node node, String name) {
-               this(node.getHostname(), node.getPort(), name);
-       }
-
-       /**
-        * Sets the name of the node.
-        *
-        * @param name
-        *            The name of the node
-        */
-       public void setName(String name) {
-               this.name = name;
-       }
-
-       /**
-        * Returns the name of the node.
-        *
-        * @return The name of the node
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Sets the hostname of the node.
-        *
-        * @param hostname
-        *            The hostname of the node
-        */
-       public void setHostname(String hostname) {
-               this.hostname = hostname;
-       }
-
-       /**
-        * Sets the port of the node.
-        *
-        * @param port
-        *            The port of the node
-        */
-       public void setPort(int port) {
-               this.port = port;
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * A node is considered as being equal to this node its name, hostname, and
-        * port equal their counterparts in this node.
-        */
-       @Override
-       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);
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * The hashcode for a node is created from its name, its hostname, and its
-        * port.
-        */
-       @Override
-       public int hashCode() {
-               return name.hashCode() ^ hostname.hashCode() ^ port;
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * Creates a textual representation of this node.
-        */
-       @Override
-       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
deleted file mode 100644 (file)
index fa0b774..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * jSite - Project.java - Copyright © 2006–2012 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 java.util.Map.Entry;
-
-import de.todesbaum.util.mime.DefaultMIMETypes;
-
-/**
- * Container for project information.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Project implements Comparable<Project> {
-
-       /** The name of the project. */
-       protected String name;
-
-       /** The description of the project. */
-       protected String description;
-
-       /** The insert URI of the project. */
-       protected String insertURI;
-
-       /** The request URI of the project. */
-       protected String requestURI;
-
-       /** The index file of the project. */
-       protected String indexFile;
-
-       /** The local path of the project. */
-       protected String localPath;
-
-       /** The remote path of the URI. */
-       protected String path;
-
-       /** The time of the last insertion. */
-       protected long lastInsertionTime;
-
-       /** The edition to insert to. */
-       protected int edition;
-
-       /** Whether to ignore hidden directory. */
-       private boolean ignoreHiddenFiles;
-
-       /** Options for files. */
-       protected Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
-
-       /**
-        * Empty constructor.
-        */
-       public Project() {
-               /* do nothing. */
-       }
-
-       /**
-        * Creates a new project from an existing one.
-        *
-        * @param project
-        *            The project to clone
-        */
-       public Project(Project project) {
-               name = project.name;
-               description = project.description;
-               insertURI = project.insertURI;
-               requestURI = project.requestURI;
-               path = project.path;
-               edition = project.edition;
-               localPath = project.localPath;
-               indexFile = project.indexFile;
-               lastInsertionTime = project.lastInsertionTime;
-               ignoreHiddenFiles = project.ignoreHiddenFiles;
-               fileOptions = new HashMap<String, FileOption>(project.fileOptions);
-       }
-
-       /**
-        * Returns the name of the project.
-        *
-        * @return The name of the project
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Sets the name of the project.
-        *
-        * @param name
-        *            The name of the project
-        */
-       public void setName(String name) {
-               this.name = name;
-       }
-
-       /**
-        * Returns the description of the project.
-        *
-        * @return The description of the project
-        */
-       public String getDescription() {
-               return description;
-       }
-
-       /**
-        * Sets the description of the project.
-        *
-        * @param description
-        *            The description of the project
-        */
-       public void setDescription(String description) {
-               this.description = description;
-       }
-
-       /**
-        * Returns the local path of the project.
-        *
-        * @return The local path of the project
-        */
-       public String getLocalPath() {
-               return localPath;
-       }
-
-       /**
-        * Sets the local path of the project.
-        *
-        * @param localPath
-        *            The local path of the project
-        */
-       public void setLocalPath(String localPath) {
-               this.localPath = localPath;
-       }
-
-       /**
-        * Returns the name of the index file of the project, relative to the
-        * project’s local path.
-        *
-        * @return The name of the index file of the project
-        */
-       public String getIndexFile() {
-               return indexFile;
-       }
-
-       /**
-        * Sets the name of the index file of the project, relative to the project’s
-        * local path.
-        *
-        * @param indexFile
-        *            The name of the index file of the project
-        */
-       public void setIndexFile(String indexFile) {
-               this.indexFile = indexFile;
-       }
-
-       /**
-        * Returns the time the project was last inserted, in milliseconds since the
-        * epoch.
-        *
-        * @return The time of the last insertion
-        */
-       public long getLastInsertionTime() {
-               return lastInsertionTime;
-       }
-
-       /**
-        * Sets the time the project was last inserted, in milliseconds since the
-        * last epoch.
-        *
-        * @param lastInserted
-        *            The time of the last insertion
-        */
-       public void setLastInsertionTime(long lastInserted) {
-               lastInsertionTime = lastInserted;
-       }
-
-       /**
-        * Returns the remote path of the project. The remote path is the path that
-        * directly follows the request URI of the project.
-        *
-        * @return The remote path of the project
-        */
-       public String getPath() {
-               return path;
-       }
-
-       /**
-        * Sets the remote path of the project. The remote path is the path that
-        * directly follows the request URI of the project.
-        *
-        * @param path
-        *            The remote path of the project
-        */
-       public void setPath(String path) {
-               this.path = path;
-       }
-
-       /**
-        * Returns the insert URI of the project.
-        *
-        * @return The insert URI of the project
-        */
-       public String getInsertURI() {
-               return insertURI;
-       }
-
-       /**
-        * Sets the insert URI of the project.
-        *
-        * @param insertURI
-        *            The insert URI of the project
-        */
-       public void setInsertURI(String insertURI) {
-               this.insertURI = shortenURI(insertURI);
-       }
-
-       /**
-        * Returns the request URI of the project.
-        *
-        * @return The request URI of the project
-        */
-       public String getRequestURI() {
-               return requestURI;
-       }
-
-       /**
-        * Sets the request URI of the project.
-        *
-        * @param requestURI
-        *            The request URI of the project
-        */
-       public void setRequestURI(String requestURI) {
-               this.requestURI = shortenURI(requestURI);
-       }
-
-       /**
-        * Returns whether hidden files are ignored, i.e. not inserted.
-        *
-        * @return {@code true} if hidden files are not inserted, {@code false}
-        *         otherwise
-        */
-       public boolean isIgnoreHiddenFiles() {
-               return ignoreHiddenFiles;
-       }
-
-       /**
-        * Sets whether hidden files are ignored, i.e. not inserted.
-        *
-        * @param ignoreHiddenFiles
-        *            {@code true} if hidden files are not inserted, {@code false}
-        *            otherwise
-        */
-       public void setIgnoreHiddenFiles(boolean ignoreHiddenFiles) {
-               this.ignoreHiddenFiles = ignoreHiddenFiles;
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * This method returns the name of the project.
-        */
-       @Override
-       public String toString() {
-               return name;
-       }
-
-       /**
-        * Shortens the given URI by removing scheme and key-type prefixes.
-        *
-        * @param uri
-        *            The URI to shorten
-        * @return The shortened URI
-        */
-       private String shortenURI(String uri) {
-               String shortUri = uri;
-               if (shortUri.startsWith("freenet:")) {
-                       shortUri = shortUri.substring("freenet:".length());
-               }
-               if (shortUri.startsWith("SSK@")) {
-                       shortUri = shortUri.substring("SSK@".length());
-               }
-               if (shortUri.startsWith("USK@")) {
-                       shortUri = shortUri.substring("USK@".length());
-               }
-               if (shortUri.endsWith("/")) {
-                       shortUri = shortUri.substring(0, shortUri.length() - 1);
-               }
-               return shortUri;
-       }
-
-       /**
-        * Shortens the name of the given file by removing the local path of the
-        * project and leading file separators.
-        *
-        * @param file
-        *            The file whose name should be shortened
-        * @return The shortened name of the file
-        */
-       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;
-       }
-
-       /**
-        * Returns the options for the file with the given name. If the file does
-        * not yet have any options, a new set of default options is created and
-        * returned.
-        *
-        * @param filename
-        *            The name of the file, relative to the project root
-        * @return The options for the file
-        */
-       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;
-       }
-
-       /**
-        * Sets options for a file.
-        *
-        * @param filename
-        *            The filename to set the options for, relative to the project
-        *            root
-        * @param fileOption
-        *            The options to set for the file, or <code>null</code> to
-        *            remove the options for the file
-        */
-       public void setFileOption(String filename, FileOption fileOption) {
-               if (fileOption != null) {
-                       fileOptions.put(filename, fileOption);
-               } else {
-                       fileOptions.remove(filename);
-               }
-       }
-
-       /**
-        * Returns all file options.
-        *
-        * @return All file options
-        */
-       public Map<String, FileOption> getFileOptions() {
-               return Collections.unmodifiableMap(fileOptions);
-       }
-
-       /**
-        * Sets all file options.
-        *
-        * @param fileOptions
-        *            The file options
-        */
-       public void setFileOptions(Map<String, FileOption> fileOptions) {
-               this.fileOptions.clear();
-               this.fileOptions.putAll(fileOptions);
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * Projects are compared by their name only.
-        */
-       public int compareTo(Project project) {
-               return name.compareToIgnoreCase(project.name);
-       }
-
-       /**
-        * 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.
-        *
-        * @param offset
-        *            The offset for the edition number
-        * @return The final request URI
-        */
-       public String getFinalRequestURI(int offset) {
-               return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/";
-       }
-
-       /**
-        * Performs some post-processing on the project after it was inserted
-        * successfully. At the moment it copies the current hashes of all file
-        * options to the last insert hashes, updating the hashes for the next
-        * insert.
-        */
-       public void onSuccessfulInsert() {
-               for (Entry<String, FileOption> fileOptionEntry : fileOptions.entrySet()) {
-                       FileOption fileOption = fileOptionEntry.getValue();
-                       if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) {
-                               fileOption.setLastInsertEdition(edition);
-                               fileOption.setLastInsertHash(fileOption.getCurrentHash());
-                               fileOption.setLastInsertFilename(fileOption.hasChangedName() ? fileOption.getChangedName() : fileOptionEntry.getKey());
-                       }
-                       fileOption.setForceInsert(false);
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/application/ProjectInserter.java b/src/de/todesbaum/jsite/application/ProjectInserter.java
deleted file mode 100644 (file)
index b5f2e16..0000000
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * jSite - ProjectInserter.java - Copyright © 2006–2012 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.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.todesbaum.jsite.gui.FileScanner;
-import de.todesbaum.jsite.gui.FileScanner.ScannedFile;
-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.ClientPutDir.ManifestPutter;
-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.PriorityClass;
-import de.todesbaum.util.freenet.fcp2.RedirectFileEntry;
-import de.todesbaum.util.freenet.fcp2.Verbosity;
-import de.todesbaum.util.io.StreamCopier.ProgressListener;
-
-/**
- * Manages project inserts.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ProjectInserter implements FileScannerListener, Runnable {
-
-       /** The logger. */
-       private static final Logger logger = Logger.getLogger(ProjectInserter.class.getName());
-
-       /** Random number for FCP instances. */
-       private static final int random = (int) (Math.random() * Integer.MAX_VALUE);
-
-       /** Counter for FCP connection identifier. */
-       private static int counter = 0;
-
-       /** The list of insert listeners. */
-       private List<InsertListener> insertListeners = new ArrayList<InsertListener>();
-
-       /** The freenet interface. */
-       protected Freenet7Interface freenetInterface;
-
-       /** The project to insert. */
-       protected Project project;
-
-       /** The file scanner. */
-       private FileScanner fileScanner;
-
-       /** Object used for synchronization. */
-       protected final Object lockObject = new Object();
-
-       /** The temp directory. */
-       private String tempDirectory;
-
-       /** The current connection. */
-       private Connection connection;
-
-       /** Whether the insert is cancelled. */
-       private volatile boolean cancelled = false;
-
-       /** Progress listener for payload transfers. */
-       private ProgressListener progressListener;
-
-       /** Whether to use “early encode.” */
-       private boolean useEarlyEncode;
-
-       /** The insert priority. */
-       private PriorityClass priority;
-
-       /** The manifest putter. */
-       private ManifestPutter manifestPutter;
-
-       /**
-        * Adds a listener to the list of registered listeners.
-        *
-        * @param insertListener
-        *            The listener to add
-        */
-       public void addInsertListener(InsertListener insertListener) {
-               insertListeners.add(insertListener);
-       }
-
-       /**
-        * Removes a listener from the list of registered listeners.
-        *
-        * @param insertListener
-        *            The listener to remove
-        */
-       public void removeInsertListener(InsertListener insertListener) {
-               insertListeners.remove(insertListener);
-       }
-
-       /**
-        * Notifies all listeners that the project insert has started.
-        *
-        * @see InsertListener#projectInsertStarted(Project)
-        */
-       protected void fireProjectInsertStarted() {
-               for (InsertListener insertListener : insertListeners) {
-                       insertListener.projectInsertStarted(project);
-               }
-       }
-
-       /**
-        * Notifies all listeners that the insert has generated a URI.
-        *
-        * @see InsertListener#projectURIGenerated(Project, String)
-        * @param uri
-        *            The generated URI
-        */
-       protected void fireProjectURIGenerated(String uri) {
-               for (InsertListener insertListener : insertListeners) {
-                       insertListener.projectURIGenerated(project, uri);
-               }
-       }
-
-       /**
-        * Notifies all listeners that the insert has made some progress.
-        *
-        * @see InsertListener#projectUploadFinished(Project)
-        */
-       protected void fireProjectUploadFinished() {
-               for (InsertListener insertListener : insertListeners) {
-                       insertListener.projectUploadFinished(project);
-               }
-       }
-
-       /**
-        * Notifies all listeners that the insert has made some progress.
-        *
-        * @see InsertListener#projectInsertProgress(Project, int, int, int, int,
-        *      boolean)
-        * @param succeeded
-        *            The number of succeeded blocks
-        * @param failed
-        *            The number of failed blocks
-        * @param fatal
-        *            The number of fatally failed blocks
-        * @param total
-        *            The total number of blocks
-        * @param finalized
-        *            <code>true</code> if the total number of blocks has already
-        *            been finalized, <code>false</code> otherwise
-        */
-       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);
-               }
-       }
-
-       /**
-        * Notifies all listeners the project insert has finished.
-        *
-        * @see InsertListener#projectInsertFinished(Project, boolean, Throwable)
-        * @param success
-        *            <code>true</code> if the project was inserted successfully,
-        *            <code>false</code> if it failed
-        * @param cause
-        *            The cause of the failure, if any
-        */
-       protected void fireProjectInsertFinished(boolean success, Throwable cause) {
-               for (InsertListener insertListener : insertListeners) {
-                       insertListener.projectInsertFinished(project, success, cause);
-               }
-       }
-
-       /**
-        * Sets the project to insert.
-        *
-        * @param project
-        *            The project to insert
-        */
-       public void setProject(Project project) {
-               this.project = project;
-       }
-
-       /**
-        * Sets the freenet interface to use.
-        *
-        * @param freenetInterface
-        *            The freenet interface to use
-        */
-       public void setFreenetInterface(Freenet7Interface freenetInterface) {
-               this.freenetInterface = freenetInterface;
-       }
-
-       /**
-        * Sets the temp directory to use.
-        *
-        * @param tempDirectory
-        *            The temp directory to use, or {@code null} to use the system
-        *            default
-        */
-       public void setTempDirectory(String tempDirectory) {
-               this.tempDirectory = tempDirectory;
-       }
-
-       /**
-        * Sets whether to use the “early encode“ flag for the insert.
-        *
-        * @param useEarlyEncode
-        *            {@code true} to set the “early encode” flag for the insert,
-        *            {@code false} otherwise
-        */
-       public void setUseEarlyEncode(boolean useEarlyEncode) {
-               this.useEarlyEncode = useEarlyEncode;
-       }
-
-       /**
-        * Sets the insert priority.
-        *
-        * @param priority
-        *            The insert priority
-        */
-       public void setPriority(PriorityClass priority) {
-               this.priority = priority;
-       }
-
-       /**
-        * Sets the manifest putter to use for inserts.
-        *
-        * @param manifestPutter
-        *            The manifest putter to use
-        */
-       public void setManifestPutter(ManifestPutter manifestPutter) {
-               this.manifestPutter = manifestPutter;
-       }
-
-       /**
-        * Starts the insert.
-        *
-        * @param progressListener
-        *            Listener to notify on progress events
-        */
-       public void start(ProgressListener progressListener) {
-               cancelled = false;
-               this.progressListener = progressListener;
-               fileScanner = new FileScanner(project);
-               fileScanner.addFileScannerListener(this);
-               new Thread(fileScanner).start();
-       }
-
-       /**
-        * Stops the current insert.
-        */
-       public void stop() {
-               cancelled = true;
-               synchronized (lockObject) {
-                       if (connection != null) {
-                               connection.disconnect();
-                       }
-               }
-       }
-
-       /**
-        * Creates an input stream that delivers the given file, replacing edition
-        * tokens in the file’s content, if necessary.
-        *
-        * @param filename
-        *            The name of the file
-        * @param fileOption
-        *            The file options
-        * @param edition
-        *            The current edition
-        * @param length
-        *            An array containing a single long which is used to
-        *            <em>return</em> the final length of the file, after all
-        *            replacements
-        * @return The input stream for the file
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException {
-               File file = new File(project.getLocalPath(), filename);
-               length[0] = file.length();
-               return new FileInputStream(file);
-       }
-
-       /**
-        * Creates a file entry suitable for handing in to
-        * {@link ClientPutComplexDir#addFileEntry(FileEntry)}.
-        *
-        * @param file
-        *            The name and hash of the file to insert
-        * @param edition
-        *            The current edition
-        * @return A file entry for the given file
-        */
-       private FileEntry createFileEntry(ScannedFile file, int edition) {
-               FileEntry fileEntry = null;
-               String filename = file.getFilename();
-               FileOption fileOption = project.getFileOption(filename);
-               if (fileOption.isInsert()) {
-                       fileOption.setCurrentHash(file.getHash());
-                       /* check if file was modified. */
-                       if (!fileOption.isForceInsert() && file.getHash().equals(fileOption.getLastInsertHash())) {
-                               /* only insert a redirect. */
-                               logger.log(Level.FINE, String.format("Inserting redirect to edition %d for %s.", fileOption.getLastInsertEdition(), filename));
-                               return new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename());
-                       }
-                       try {
-                               long[] fileLength = new long[1];
-                               InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength);
-                               fileEntry = new DirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileEntryInputStream, fileLength[0]);
-                       } catch (IOException ioe1) {
-                               /* ignore, null is returned. */
-                       }
-               } else {
-                       if (fileOption.isInsertRedirect()) {
-                               fileEntry = new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileOption.getCustomKey());
-                       }
-               }
-               return fileEntry;
-       }
-
-       /**
-        * Validates the given project. The project will be checked for any invalid
-        * conditions, such as invalid insert or request keys, missing path names,
-        * missing default file, and so on.
-        *
-        * @param project
-        *            The project to check
-        * @return The encountered warnings and errors
-        */
-       public static CheckReport validateProject(Project project) {
-               CheckReport checkReport = new CheckReport();
-               if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) {
-                       checkReport.addIssue("error.no-local-path", true);
-               }
-               if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
-                       checkReport.addIssue("error.no-path", true);
-               }
-               if ((project.getIndexFile() == null) || (project.getIndexFile().length() == 0)) {
-                       checkReport.addIssue("warning.empty-index", false);
-               } else {
-                       File indexFile = new File(project.getLocalPath(), project.getIndexFile());
-                       if (!indexFile.exists()) {
-                               checkReport.addIssue("error.index-missing", true);
-                       }
-               }
-               String indexFile = project.getIndexFile();
-               boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0);
-               List<String> allowedIndexContentTypes = Arrays.asList("text/html", "application/xhtml+xml");
-               if (hasIndexFile && !allowedIndexContentTypes.contains(project.getFileOption(indexFile).getMimeType())) {
-                       checkReport.addIssue("warning.index-not-html", false);
-               }
-               Map<String, FileOption> fileOptions = project.getFileOptions();
-               Set<Entry<String, FileOption>> fileOptionEntries = fileOptions.entrySet();
-               boolean insert = fileOptionEntries.isEmpty();
-               for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
-                       String fileName = fileOptionEntry.getKey();
-                       FileOption fileOption = fileOptionEntry.getValue();
-                       insert |= fileOption.isInsert() || fileOption.isInsertRedirect();
-                       if (fileName.equals(project.getIndexFile()) && !fileOption.isInsert() && !fileOption.isInsertRedirect()) {
-                               checkReport.addIssue("error.index-not-inserted", true);
-                       }
-                       if (!fileOption.isInsert() && fileOption.isInsertRedirect() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(fileOption.getCustomKey()))) {
-                               checkReport.addIssue("error.no-custom-key", true, fileName);
-                       }
-               }
-               if (!insert) {
-                       checkReport.addIssue("error.no-files-to-insert", true);
-               }
-               Set<String> fileNames = new HashSet<String>();
-               for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
-                       FileOption fileOption = fileOptionEntry.getValue();
-                       if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) {
-                               logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey());
-                               continue;
-                       }
-                       String fileName = fileOptionEntry.getKey();
-                       if (fileOption.hasChangedName()) {
-                               fileName = fileOption.getChangedName();
-                       }
-                       logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() });
-                       if (!fileNames.add(fileName)) {
-                               checkReport.addIssue("error.duplicate-file", true, fileName);
-                       }
-               }
-               long totalSize = 0;
-               FileScanner fileScanner = new FileScanner(project);
-               final CountDownLatch completionLatch = new CountDownLatch(1);
-               fileScanner.addFileScannerListener(new FileScannerListener() {
-
-                       @Override
-                       public void fileScannerFinished(FileScanner fileScanner) {
-                               completionLatch.countDown();
-                       }
-               });
-               new Thread(fileScanner).start();
-               while (completionLatch.getCount() > 0) {
-                       try {
-                               completionLatch.await();
-                       } catch (InterruptedException ie1) {
-                               /* TODO: logging */
-                       }
-               }
-               for (ScannedFile scannedFile : fileScanner.getFiles()) {
-                       String fileName = scannedFile.getFilename();
-                       FileOption fileOption = project.getFileOption(fileName);
-                       if ((fileOption != null) && !fileOption.isInsert()) {
-                               continue;
-                       }
-                       totalSize += new File(project.getLocalPath(), fileName).length();
-               }
-               if (totalSize > 2 * 1024 * 1024) {
-                       checkReport.addIssue("warning.site-larger-than-2-mib", false);
-               }
-               return checkReport;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void run() {
-               fireProjectInsertStarted();
-               List<ScannedFile> files = fileScanner.getFiles();
-
-               /* create connection to node */
-               synchronized (lockObject) {
-                       connection = freenetInterface.getConnection("project-insert-" + random + counter++);
-               }
-               connection.setTempDirectory(tempDirectory);
-               boolean connected = false;
-               Throwable cause = null;
-               try {
-                       connected = connection.connect();
-               } catch (IOException e1) {
-                       cause = e1;
-               }
-
-               if (!connected || cancelled) {
-                       fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause);
-                       return;
-               }
-
-               Client client = new Client(connection);
-
-               /* collect files */
-               int edition = project.getEdition();
-               String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/";
-               ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI, tempDirectory);
-               if ((project.getIndexFile() != null) && (project.getIndexFile().length() > 0)) {
-                       putDir.setDefaultName(project.getIndexFile());
-               }
-               putDir.setVerbosity(Verbosity.ALL);
-               putDir.setMaxRetries(-1);
-               putDir.setEarlyEncode(useEarlyEncode);
-               putDir.setPriorityClass(priority);
-               putDir.setManifestPutter(manifestPutter);
-               for (ScannedFile file : files) {
-                       FileEntry fileEntry = createFileEntry(file, edition);
-                       if (fileEntry != null) {
-                               try {
-                                       putDir.addFileEntry(fileEntry);
-                               } catch (IOException ioe1) {
-                                       fireProjectInsertFinished(false, ioe1);
-                                       return;
-                               }
-                       }
-               }
-
-               /* start request */
-               try {
-                       client.execute(putDir, progressListener);
-                       fireProjectUploadFinished();
-               } catch (IOException ioe1) {
-                       fireProjectInsertFinished(false, ioe1);
-                       return;
-               }
-
-               /* parse progress and success messages */
-               String finalURI = null;
-               boolean success = false;
-               boolean finished = false;
-               boolean disconnected = false;
-               while (!finished && !cancelled) {
-                       Message message = client.readMessage();
-                       finished = (message == null) || (disconnected = client.isDisconnected());
-                       logger.log(Level.FINE, "Received message: " + message);
-                       if (!finished) {
-                               @SuppressWarnings("null")
-                               String messageName = message.getName();
-                               if ("URIGenerated".equals(messageName)) {
-                                       finalURI = message.get("URI");
-                                       fireProjectURIGenerated(finalURI);
-                               }
-                               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 && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error");
-                       }
-               }
-
-               /* post-insert work */
-               if (success) {
-                       @SuppressWarnings("null")
-                       String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1);
-                       int newEdition = Integer.parseInt(editionPart);
-                       project.setEdition(newEdition);
-                       project.setLastInsertionTime(System.currentTimeMillis());
-                       project.onSuccessfulInsert();
-               }
-               fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null));
-       }
-
-       //
-       // INTERFACE FileScannerListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void fileScannerFinished(FileScanner fileScanner) {
-               if (!fileScanner.isError()) {
-                       new Thread(this).start();
-               } else {
-                       fireProjectInsertFinished(false, null);
-               }
-               fileScanner.removeFileScannerListener(this);
-       }
-
-       /**
-        * Container class that collects all warnings and errors that occured during
-        * {@link ProjectInserter#validateProject(Project) project validation}.
-        *
-        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
-        */
-       public static class CheckReport implements Iterable<Issue> {
-
-               /** The issures that occured. */
-               private final List<Issue> issues = new ArrayList<Issue>();
-
-               /**
-                * Adds an issue.
-                *
-                * @param issue
-                *            The issue to add
-                */
-               public void addIssue(Issue issue) {
-                       issues.add(issue);
-               }
-
-               /**
-                * Creates an {@link Issue} from the given error key and fatality flag
-                * and {@link #addIssue(Issue) adds} it.
-                *
-                * @param errorKey
-                *            The error key
-                * @param fatal
-                *            {@code true} if the error is fatal, {@code false} if only
-                *            a warning should be generated
-                * @param parameters
-                *            Any additional parameters
-                */
-               public void addIssue(String errorKey, boolean fatal, String... parameters) {
-                       addIssue(new Issue(errorKey, fatal, parameters));
-               }
-
-               /**
-                * {@inheritDoc}
-                */
-               public Iterator<Issue> iterator() {
-                       return issues.iterator();
-               }
-
-               /**
-                * Returns whether this check report does not contain any errors.
-                *
-                * @return {@code true} if this check report does not contain any
-                *         errors, {@code false} if this check report does contain
-                *         errors
-                */
-               public boolean isEmpty() {
-                       return issues.isEmpty();
-               }
-
-               /**
-                * Returns the number of issues in this check report.
-                *
-                * @return The number of issues
-                */
-               public int size() {
-                       return issues.size();
-               }
-
-       }
-
-       /**
-        * Container class for a single issue. An issue contains an error key
-        * that describes the error, and a fatality flag that determines whether
-        * the insert has to be aborted (if the flag is {@code true}) or if it
-        * can still be performed and only a warning should be generated (if the
-        * flag is {@code false}).
-        *
-        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
-        *         Roden</a>
-        */
-       public static class Issue {
-
-               /** The error key. */
-               private final String errorKey;
-
-               /** The fatality flag. */
-               private final boolean fatal;
-
-               /** Additional parameters. */
-               private String[] parameters;
-
-               /**
-                * Creates a new issue.
-                *
-                * @param errorKey
-                *            The error key
-                * @param fatal
-                *            The fatality flag
-                * @param parameters
-                *            Any additional parameters
-                */
-               protected Issue(String errorKey, boolean fatal, String... parameters) {
-                       this.errorKey = errorKey;
-                       this.fatal = fatal;
-                       this.parameters = parameters;
-               }
-
-               /**
-                * Returns the key of the encountered error.
-                *
-                * @return The error key
-                */
-               public String getErrorKey() {
-                       return errorKey;
-               }
-
-               /**
-                * Returns whether the issue is fatal and the insert has to be
-                * aborted. Otherwise only a warning should be shown.
-                *
-                * @return {@code true} if the insert needs to be aborted, {@code
-                *         false} otherwise
-                */
-               public boolean isFatal() {
-                       return fatal;
-               }
-
-               /**
-                * Returns any additional parameters.
-                *
-                * @return The additional parameters
-                */
-               public String[] getParameters() {
-                       return parameters;
-               }
-
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/application/UpdateChecker.java b/src/de/todesbaum/jsite/application/UpdateChecker.java
deleted file mode 100644 (file)
index c49f166..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * jSite - UpdateChecker.java - Copyright © 2008–2012 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 java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.todesbaum.jsite.main.Main;
-import de.todesbaum.jsite.main.Version;
-import de.todesbaum.util.freenet.fcp2.Client;
-import de.todesbaum.util.freenet.fcp2.ClientGet;
-import de.todesbaum.util.freenet.fcp2.Connection;
-import de.todesbaum.util.freenet.fcp2.Message;
-import de.todesbaum.util.freenet.fcp2.Persistence;
-import de.todesbaum.util.freenet.fcp2.ReturnType;
-import de.todesbaum.util.freenet.fcp2.Verbosity;
-import de.todesbaum.util.io.Closer;
-
-/**
- * Checks for newer versions of jSite.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class UpdateChecker implements Runnable {
-
-       /** The logger. */
-       private static final Logger logger = Logger.getLogger(UpdateChecker.class.getName());
-
-       /** Counter for connection names. */
-       private static int counter = 0;
-
-       /** The edition for the update check URL. */
-       private static final int UPDATE_EDITION = 17;
-
-       /** The URL for update checks. */
-       private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE";
-
-       /** Object used for synchronization. */
-       private final Object syncObject = new Object();
-
-       /** Update listeners. */
-       private final List<UpdateListener> updateListeners = new ArrayList<UpdateListener>();
-
-       /** Whether the main thread should stop. */
-       private boolean shouldStop = false;
-
-       /** Current last found edition of update key. */
-       private int lastUpdateEdition = UPDATE_EDITION;
-
-       /** Last found version. */
-       private Version lastVersion = Main.getVersion();
-
-       /** The freenet interface. */
-       private final Freenet7Interface freenetInterface;
-
-       /**
-        * Creates a new update checker that uses the given frame as its parent and
-        * communications via the given freenet interface.
-        *
-        * @param freenetInterface
-        *            The freenet interface
-        */
-       public UpdateChecker(Freenet7Interface freenetInterface) {
-               this.freenetInterface = freenetInterface;
-       }
-
-       //
-       // EVENT LISTENER MANAGEMENT
-       //
-
-       /**
-        * Adds an update listener to the list of registered listeners.
-        *
-        * @param updateListener
-        *            The update listener to add
-        */
-       public void addUpdateListener(UpdateListener updateListener) {
-               updateListeners.add(updateListener);
-       }
-
-       /**
-        * Removes the given listener from the list of registered listeners.
-        *
-        * @param updateListener
-        *            The update listener to remove
-        */
-       public void removeUpdateListener(UpdateListener updateListener) {
-               updateListeners.remove(updateListener);
-       }
-
-       /**
-        * Notifies all listeners that a version was found.
-        *
-        * @param foundVersion
-        *            The version that was found
-        * @param versionTimestamp
-        *            The timestamp of the version
-        */
-       protected void fireUpdateFound(Version foundVersion, long versionTimestamp) {
-               for (UpdateListener updateListener : updateListeners) {
-                       updateListener.foundUpdateData(foundVersion, versionTimestamp);
-               }
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the latest version that was found.
-        *
-        * @return The latest found version
-        */
-       public Version getLatestVersion() {
-               return lastVersion;
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Starts the update checker.
-        */
-       public void start() {
-               new Thread(this).start();
-       }
-
-       /**
-        * Stops the update checker.
-        */
-       public void stop() {
-               synchronized (syncObject) {
-                       shouldStop = true;
-                       syncObject.notifyAll();
-               }
-       }
-
-       //
-       // PRIVATE METHODS
-       //
-
-       /**
-        * Returns whether the update checker should stop.
-        *
-        * @return <code>true</code> if the update checker should stop,
-        *         <code>false</code> otherwise
-        */
-       private boolean shouldStop() {
-               synchronized (syncObject) {
-                       return shouldStop;
-               }
-       }
-
-       /**
-        * Creates the URI of the update file for the given edition.
-        *
-        * @param edition
-        *            The edition number
-        * @return The URI for the update file for the given edition
-        */
-       private String constructUpdateKey(int edition) {
-               return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties";
-       }
-
-       //
-       // INTERFACE Runnable
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void run() {
-               Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker");
-               try {
-                       connection.connect();
-               } catch (IOException e1) {
-                       e1.printStackTrace();
-               }
-               Client client = new Client(connection);
-               boolean checkNow = false;
-               int currentEdition = lastUpdateEdition;
-               while (!shouldStop()) {
-                       checkNow = false;
-                       logger.log(Level.FINE, "Trying " + constructUpdateKey(currentEdition));
-                       ClientGet clientGet = new ClientGet("get-update-key");
-                       clientGet.setUri(constructUpdateKey(currentEdition));
-                       clientGet.setPersistence(Persistence.CONNECTION);
-                       clientGet.setReturnType(ReturnType.direct);
-                       clientGet.setVerbosity(Verbosity.ALL);
-                       try {
-                               client.execute(clientGet);
-                               boolean stop = false;
-                               while (!stop) {
-                                       Message message = client.readMessage();
-                                       logger.log(Level.FINEST, "Received message: " + message);
-                                       if (message == null) {
-                                               break;
-                                       }
-                                       if ("GetFailed".equals(message.getName())) {
-                                               if ("27".equals(message.get("code"))) {
-                                                       String editionString = message.get("redirecturi").split("/")[2];
-                                                       int editionNumber = -1;
-                                                       try {
-                                                               editionNumber = Integer.parseInt(editionString);
-                                                       } catch (NumberFormatException nfe1) {
-                                                               /* ignore. */
-                                                       }
-                                                       if (editionNumber != -1) {
-                                                               logger.log(Level.INFO, "Found new edition " + editionNumber);
-                                                               currentEdition = editionNumber;
-                                                               lastUpdateEdition = editionNumber;
-                                                               checkNow = true;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       if ("AllData".equals(message.getName())) {
-                                               logger.log(Level.FINE, "Update data found.");
-                                               InputStream dataInputStream = null;
-                                               Properties properties = new Properties();
-                                               try {
-                                                       dataInputStream = message.getPayloadInputStream();
-                                                       properties.load(dataInputStream);
-                                               } finally {
-                                                       Closer.close(dataInputStream);
-                                               }
-
-                                               String foundVersionString = properties.getProperty("jSite.Version");
-                                               if (foundVersionString != null) {
-                                                       Version foundVersion = Version.parse(foundVersionString);
-                                                       if (foundVersion != null) {
-                                                               lastVersion = foundVersion;
-                                                               String versionTimestampString = properties.getProperty("jSite.Date");
-                                                               logger.log(Level.FINEST, "Version timestamp: " + versionTimestampString);
-                                                               long versionTimestamp = -1;
-                                                               try {
-                                                                       versionTimestamp = Long.parseLong(versionTimestampString);
-                                                               } catch (NumberFormatException nfe1) {
-                                                                       /* ignore. */
-                                                               }
-                                                               fireUpdateFound(foundVersion, versionTimestamp);
-                                                               stop = true;
-                                                               checkNow = true;
-                                                               ++currentEdition;
-                                                       }
-                                               }
-                                       }
-                               }
-                       } catch (IOException e) {
-                               logger.log(Level.INFO, "Got IOException: " + e.getMessage());
-                               e.printStackTrace();
-                       }
-                       if (!checkNow && !shouldStop()) {
-                               synchronized (syncObject) {
-                                       try {
-                                               syncObject.wait(15 * 60 * 1000);
-                                       } catch (InterruptedException ie1) {
-                                               /* ignore. */
-                                       }
-                               }
-                       }
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/application/UpdateListener.java b/src/de/todesbaum/jsite/application/UpdateListener.java
deleted file mode 100644 (file)
index a37440c..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * jSite - UpdateListener.java - Copyright © 2008–2012 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;
-
-import de.todesbaum.jsite.main.Version;
-
-/**
- * Listener interface for objects that want to be notified when update data was
- * found.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public interface UpdateListener extends EventListener {
-
-       /**
-        * Notifies a listener that data for the given version was found.
-        *
-        * @param foundVersion
-        *            The version that was found
-        * @param versionTimestamp
-        *            The timestamp of the version, or <code>-1</code> if the
-        *            timestamp is unknown
-        */
-       public void foundUpdateData(Version foundVersion, long versionTimestamp);
-
-}
diff --git a/src/de/todesbaum/jsite/gui/FileScanner.java b/src/de/todesbaum/jsite/gui/FileScanner.java
deleted file mode 100644 (file)
index 1790fbf..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * jSite - FileScanner.java - Copyright © 2006–2012 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.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.todesbaum.jsite.application.Project;
-import de.todesbaum.jsite.i18n.I18n;
-import de.todesbaum.util.io.Closer;
-import de.todesbaum.util.io.StreamCopier;
-
-/**
- * Scans the local path of a project anychronously and returns the list of found
- * files as an event.
- *
- * @see Project#getLocalPath()
- * @see FileScannerListener#fileScannerFinished(FileScanner)
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class FileScanner implements Runnable {
-
-       /** The logger. */
-       private final static Logger logger = Logger.getLogger(FileScanner.class.getName());
-
-       /** The list of listeners. */
-       private final List<FileScannerListener> fileScannerListeners = new ArrayList<FileScannerListener>();
-
-       /** The project to scan. */
-       private final Project project;
-
-       /** The list of found files. */
-       private List<ScannedFile> files;
-
-       /** Wether there was an error. */
-       private boolean error = false;
-
-       /**
-        * Creates a new file scanner for the given project.
-        *
-        * @param project
-        *            The project whose files to scan
-        */
-       public FileScanner(Project project) {
-               this.project = project;
-       }
-
-       /**
-        * Adds the given listener to the list of listeners.
-        *
-        * @param fileScannerListener
-        *            The listener to add
-        */
-       public void addFileScannerListener(FileScannerListener fileScannerListener) {
-               fileScannerListeners.add(fileScannerListener);
-       }
-
-       /**
-        * Removes the given listener from the list of listeners.
-        *
-        * @param fileScannerListener
-        *            The listener to remove
-        */
-       public void removeFileScannerListener(FileScannerListener fileScannerListener) {
-               fileScannerListeners.remove(fileScannerListener);
-       }
-
-       /**
-        * Notifies all listeners that the file scan finished.
-        */
-       protected void fireFileScannerFinished() {
-               for (FileScannerListener fileScannerListener : new ArrayList<FileScannerListener>(fileScannerListeners)) {
-                       fileScannerListener.fileScannerFinished(this);
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * Scans all available files in the project’s local path and emits an event
-        * when finished.
-        *
-        * @see FileScannerListener#fileScannerFinished(FileScanner)
-        */
-       public void run() {
-               files = new ArrayList<ScannedFile>();
-               error = false;
-               try {
-                       scanFiles(new File(project.getLocalPath()), files);
-                       Collections.sort(files);
-               } catch (IOException ioe1) {
-                       error = true;
-               }
-               fireFileScannerFinished();
-       }
-
-       /**
-        * Returns whether there was an error scanning for files.
-        *
-        * @return <code>true</code> if there was an error, <code>false</code>
-        *         otherwise
-        */
-       public boolean isError() {
-               return error;
-       }
-
-       /**
-        * Returns the list of found files.
-        *
-        * @return The list of found files
-        */
-       public List<ScannedFile> getFiles() {
-               return files;
-       }
-
-       /**
-        * Recursively scans a directory and adds all found files to the given list.
-        *
-        * @param rootDir
-        *            The directory to scan
-        * @param fileList
-        *            The list to which to add the found files
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       private void scanFiles(File rootDir, List<ScannedFile> fileList) throws IOException {
-               File[] files = rootDir.listFiles(new FileFilter() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public boolean accept(File file) {
-                               return !project.isIgnoreHiddenFiles() || !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).replace('\\', '/');
-                       String hash = hashFile(project.getLocalPath(), filename);
-                       fileList.add(new ScannedFile(filename, hash));
-               }
-       }
-
-       /**
-        * Hashes the given file.
-        *
-        * @param path
-        *            The path of the project
-        * @param filename
-        *            The name of the file, relative to the project path
-        * @return The hash of the file
-        */
-       @SuppressWarnings("synthetic-access")
-       private static String hashFile(String path, String filename) {
-               InputStream fileInputStream = null;
-               DigestOutputStream digestOutputStream = null;
-               File file = new File(path, filename);
-               try {
-                       fileInputStream = new FileInputStream(file);
-                       digestOutputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256"));
-                       StreamCopier.copy(fileInputStream, digestOutputStream, file.length());
-                       return toHex(digestOutputStream.getMessageDigest().digest());
-               } catch (NoSuchAlgorithmException nsae1) {
-                       logger.log(Level.WARNING, "Could not get SHA-256 digest!", nsae1);
-               } catch (IOException ioe1) {
-                       logger.log(Level.WARNING, "Could not read file!", ioe1);
-               } finally {
-                       Closer.close(digestOutputStream);
-                       Closer.close(fileInputStream);
-               }
-               return toHex(new byte[32]);
-       }
-
-       /**
-        * Converts the given byte array into a hexadecimal string.
-        *
-        * @param array
-        *            The array to convert
-        * @return The hexadecimal string
-        */
-       private static String toHex(byte[] array) {
-               StringBuilder hexString = new StringBuilder(array.length * 2);
-               for (byte b : array) {
-                       hexString.append("0123456789abcdef".charAt((b >>> 4) & 0x0f)).append("0123456789abcdef".charAt(b & 0xf));
-               }
-               return hexString.toString();
-       }
-
-       /**
-        * {@link OutputStream} that discards all written bytes.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       private static class NullOutputStream extends OutputStream {
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public void write(int b) {
-                       /* do nothing. */
-               }
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public void write(byte[] b) {
-                       /* do nothing. */
-               }
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public void write(byte[] b, int off, int len) {
-                       /* do nothing. */
-               }
-
-       }
-
-       /**
-        * Container for a scanned file, consisting of the name of the file and its
-        * hash.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       public static class ScannedFile implements Comparable<ScannedFile> {
-
-               /** The name of the file. */
-               private final String filename;
-
-               /** The hash of the file. */
-               private final String hash;
-
-               /**
-                * Creates a new scanned file.
-                *
-                * @param filename
-                *            The name of the file
-                * @param hash
-                *            The hash of the file
-                */
-               public ScannedFile(String filename, String hash) {
-                       this.filename = filename;
-                       this.hash = hash;
-               }
-
-               //
-               // ACCESSORS
-               //
-
-               /**
-                * Returns the name of the file.
-                *
-                * @return The name of the file
-                */
-               public String getFilename() {
-                       return filename;
-               }
-
-               /**
-                * Returns the hash of the file.
-                *
-                * @return The hash of the file
-                */
-               public String getHash() {
-                       return hash;
-               }
-
-               //
-               // OBJECT METHODS
-               //
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public int hashCode() {
-                       return filename.hashCode();
-               }
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public boolean equals(Object obj) {
-                       return filename.equals(obj);
-               }
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public String toString() {
-                       return filename;
-               }
-
-               //
-               // COMPARABLE METHODS
-               //
-
-               /**
-                * {@inheritDoc}
-                */
-               public int compareTo(ScannedFile scannedFile) {
-                       return filename.compareTo(scannedFile.filename);
-               }
-
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/gui/FileScannerListener.java b/src/de/todesbaum/jsite/gui/FileScannerListener.java
deleted file mode 100644 (file)
index 63e2c3d..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * jSite - FileScannerListener.java - Copyright © 2006–2012 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;
-
-/**
- * Listener interface for objects that want to be notified when scanning a
- * project’s local path has finished.
- *
- * @see FileScanner
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public interface FileScannerListener extends EventListener {
-
-       /**
-        * Notifies a listener that scanning a project’s local path has finished.
-        *
-        * @param fileScanner
-        *            The file scanner that finished
-        */
-       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
deleted file mode 100644 (file)
index 760c967..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * jSite - NodeManagerListener.java - Copyright © 2006–2012 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;
-
-/**
- * Listener interface for objects that want to be notified if the node
- * configuration changes.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public interface NodeManagerListener extends EventListener {
-
-       /**
-        * Notifies a listener that the node configuration was changed.
-        *
-        * @param nodes
-        *            The new list of nodes
-        */
-       public void nodesUpdated(Node[] nodes);
-
-       /**
-        * Notifies a listener that the selected node has changed.
-        *
-        * @param node
-        *            The new selected node
-        */
-       public void nodeSelected(Node node);
-
-}
diff --git a/src/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/de/todesbaum/jsite/gui/NodeManagerPage.java
deleted file mode 100644 (file)
index 8e44129..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * jSite - NodeManagerPage.java - Copyright © 2006–2012 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.jsite.i18n.I18nContainer;
-import de.todesbaum.util.swing.TLabel;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-
-/**
- * Wizard page that lets the user edit his nodes.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class NodeManagerPage extends TWizardPage implements ListSelectionListener, DocumentListener, ChangeListener {
-
-       /** List of node manager listeners. */
-       private List<NodeManagerListener> nodeManagerListeners = new ArrayList<NodeManagerListener>();
-
-       /** The “add node” action. */
-       protected Action addNodeAction;
-
-       /** The “delete node” action. */
-       protected Action deleteNodeAction;
-
-       /** The node list model. */
-       private DefaultListModel nodeListModel;
-
-       /** The node list. */
-       private JList nodeList;
-
-       /** The node name textfield. */
-       private JTextField nodeNameTextField;
-
-       /** The node hostname textfield. */
-       private JTextField nodeHostnameTextField;
-
-       /** The spinner for the node port. */
-       private JSpinner nodePortSpinner;
-
-       /**
-        * Creates a new node manager wizard page.
-        *
-        * @param wizard
-        *            The wizard this page belongs to
-        */
-       public NodeManagerPage(final TWizard wizard) {
-               super(wizard);
-               pageInit();
-               setHeading(I18n.getMessage("jsite.node-manager.heading"));
-               setDescription(I18n.getMessage("jsite.node-manager.description"));
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               setHeading(I18n.getMessage("jsite.node-manager.heading"));
-                               setDescription(I18n.getMessage("jsite.node-manager.description"));
-                       }
-               });
-       }
-
-       /**
-        * Adds a listener for node manager events.
-        *
-        * @param nodeManagerListener
-        *            The listener to add
-        */
-       public void addNodeManagerListener(NodeManagerListener nodeManagerListener) {
-               nodeManagerListeners.add(nodeManagerListener);
-       }
-
-       /**
-        * Removes a listener for node manager events.
-        *
-        * @param nodeManagerListener
-        *            The listener to remove
-        */
-       public void removeNodeManagerListener(NodeManagerListener nodeManagerListener) {
-               nodeManagerListeners.remove(nodeManagerListener);
-       }
-
-       /**
-        * Notifies all listeners that the node configuration has changed.
-        *
-        * @param nodes
-        *            The new list of nodes
-        */
-       protected void fireNodesUpdated(Node[] nodes) {
-               for (NodeManagerListener nodeManagerListener : nodeManagerListeners) {
-                       nodeManagerListener.nodesUpdated(nodes);
-               }
-       }
-
-       /**
-        * Notifies all listeners that a new node was selected.
-        *
-        * @param node
-        *            The newly selected node
-        */
-       protected void fireNodeSelected(Node node) {
-               for (NodeManagerListener nodeManagerListener : nodeManagerListeners) {
-                       nodeManagerListener.nodeSelected(node);
-               }
-       }
-
-       /**
-        * Creates all actions.
-        */
-       private void createActions() {
-               addNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.add-node")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               addNode();
-                       }
-               };
-
-               deleteNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.delete-node")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               deleteNode();
-                       }
-               };
-               deleteNodeAction.setEnabled(false);
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               addNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.add-node"));
-                               deleteNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.delete-node"));
-                       }
-               });
-       }
-
-       /**
-        * Initializes the page and all components in it.
-        */
-       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));
-               final JLabel nodeInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.node-manager.node-information") + "</b></html>");
-               nodeInformationPanel.add(nodeInformationLabel, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0));
-               final TLabel nodeNameLabel = new TLabel(I18n.getMessage("jsite.node-manager.name") + ":", KeyEvent.VK_N, nodeNameTextField);
-               nodeInformationPanel.add(nodeNameLabel, 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));
-               final TLabel nodeHostnameLabel = new TLabel(I18n.getMessage("jsite.node-manager.hostname") + ":", KeyEvent.VK_H, nodeHostnameTextField);
-               nodeInformationPanel.add(nodeHostnameLabel, 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));
-               final TLabel nodePortLabel = new TLabel(I18n.getMessage("jsite.node-manager.port") + ":", KeyEvent.VK_P, nodePortSpinner);
-               nodeInformationPanel.add(nodePortLabel, 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);
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               nodeInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.node-manager.node-information") + "</b></html>");
-                               nodeNameLabel.setText(I18n.getMessage("jsite.node-manager.name") + ":");
-                               nodeHostnameLabel.setText(I18n.getMessage("jsite.node-manager.hostname") + ":");
-                               nodePortLabel.setText(I18n.getMessage("jsite.node-manager.port") + ":");
-                       }
-               });
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pageAdded(TWizard wizard) {
-               this.wizard.setNextEnabled(nodeListModel.getSize() > 0);
-               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
-               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
-       }
-
-       /**
-        * Sets the node list.
-        *
-        * @param nodes
-        *            The list of nodes
-        */
-       public void setNodes(Node[] nodes) {
-               nodeListModel.clear();
-               for (Node node : nodes) {
-                       nodeListModel.addElement(node);
-               }
-               nodeList.repaint();
-               fireNodesUpdated(nodes);
-       }
-
-       /**
-        * Returns the node list.
-        *
-        * @return The list of 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;
-       }
-
-       /**
-        * Returns the currently selected node.
-        *
-        * @return The selected node, or <code>null</code> if no node is selected
-        */
-       private Node getSelectedNode() {
-               return (Node) nodeList.getSelectedValue();
-       }
-
-       /**
-        * Updates node name or hostname when the user types into the textfields.
-        *
-        * @see #insertUpdate(DocumentEvent)
-        * @see #removeUpdate(DocumentEvent)
-        * @see #changedUpdate(DocumentEvent)
-        * @see DocumentListener
-        * @param documentEvent
-        *            The document event
-        */
-       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) {
-                       /* ignore. */
-               }
-               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();
-                       fireNodesUpdated(getNodes());
-               }
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Adds a new node to the list of nodes.
-        */
-       private void addNode() {
-               Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node"));
-               nodeListModel.addElement(node);
-               deleteNodeAction.setEnabled(nodeListModel.size() > 1);
-               wizard.setNextEnabled(true);
-               fireNodesUpdated(getNodes());
-       }
-
-       /**
-        * Deletes the currently selected node from the list of nodes.
-        */
-       private 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;
-               }
-               int nodeIndex = nodeListModel.indexOf(node);
-               nodeListModel.removeElement(node);
-               nodeList.repaint();
-               fireNodeSelected((Node) nodeListModel.get(Math.min(nodeIndex, nodeListModel.size() - 1)));
-               fireNodesUpdated(getNodes());
-               deleteNodeAction.setEnabled(nodeListModel.size() > 1);
-               wizard.setNextEnabled(nodeListModel.size() > 0);
-       }
-
-       //
-       // INTERFACE ListSelectionListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @SuppressWarnings("null")
-       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 && (nodeListModel.size() > 1));
-                               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());
-                               fireNodeSelected(selectedNode);
-                               nodeList.repaint();
-                       }
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/gui/PreferencesPage.java b/src/de/todesbaum/jsite/gui/PreferencesPage.java
deleted file mode 100644 (file)
index 5cf258a..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * jSite - PreferencesPage.java - Copyright © 2009–2012 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.awt.event.ActionEvent;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.BorderFactory;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JTextField;
-
-import de.todesbaum.jsite.i18n.I18n;
-import de.todesbaum.jsite.i18n.I18nContainer;
-import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
-import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
-import de.todesbaum.util.freenet.fcp2.PriorityClass;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-
-/**
- * Page that shows some preferences that are valid for the complete application.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class PreferencesPage extends TWizardPage {
-
-       /** Select default temp directory action. */
-       private Action selectDefaultTempDirectoryAction;
-
-       /** Select custom temp directory action. */
-       private Action selectCustomTempDirectoryAction;
-
-       /** Action that chooses a new temp directory. */
-       private Action chooseTempDirectoryAction;
-
-       /** Action when selecting “next to JAR file.” */
-       private Action nextToJarFileAction;
-
-       /** Action when selecting “home directory.” */
-       private Action homeDirectoryAction;
-
-       /** Action when selecting “custom directory.” */
-       private Action customDirectoryAction;
-
-       /** Action when selecting “use early encode.” */
-       private Action useEarlyEncodeAction;
-
-       /** Action when a priority was selected. */
-       private Action priorityAction;
-
-       /** The text field containing the directory. */
-       private JTextField tempDirectoryTextField;
-
-       /** The temp directory. */
-       private String tempDirectory;
-
-       /** The configuration location. */
-       private ConfigurationLocation configurationLocation;
-
-       /** Whether to use “early encode.” */
-       private boolean useEarlyEncode;
-
-       /** The prioriy for inserts. */
-       private PriorityClass priority;
-
-       /** The “default” button. */
-       private JRadioButton defaultTempDirectory;
-
-       /** The “custom” button. */
-       private JRadioButton customTempDirectory;
-
-       /** The “next to JAR file” checkbox. */
-       private JRadioButton nextToJarFile;
-
-       /** The “home directory” checkbox. */
-       private JRadioButton homeDirectory;
-
-       /** The “custom directory” checkbox. */
-       private JRadioButton customDirectory;
-
-       /** The “use early encode” checkbox. */
-       private JCheckBox useEarlyEncodeCheckBox;
-
-       /** The insert priority select box. */
-       private JComboBox insertPriorityComboBox;
-
-       /** The manifest putter select box. */
-       private JComboBox manifestPutterComboBox;
-
-       /**
-        * Creates a new “preferences” page.
-        *
-        * @param wizard
-        *            The wizard this page belongs to
-        */
-       public PreferencesPage(TWizard wizard) {
-               super(wizard);
-               pageInit();
-               setHeading(I18n.getMessage("jsite.preferences.heading"));
-               setDescription(I18n.getMessage("jsite.preferences.description"));
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       public void run() {
-                               setHeading(I18n.getMessage("jsite.preferences.heading"));
-                               setDescription(I18n.getMessage("jsite.preferences.description"));
-                       }
-               });
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the temp directory.
-        *
-        * @return The temp directory, or {@code null} to use the default temp
-        *         directory
-        */
-       public String getTempDirectory() {
-               return tempDirectory;
-       }
-
-       /**
-        * Sets the temp directory.
-        *
-        * @param tempDirectory
-        *            The temp directory, or {@code null} to use the default temp
-        *            directory
-        */
-       public void setTempDirectory(String tempDirectory) {
-               this.tempDirectory = tempDirectory;
-               tempDirectoryTextField.setText((tempDirectory != null) ? tempDirectory : "");
-               if (tempDirectory != null) {
-                       customTempDirectory.setSelected(true);
-                       chooseTempDirectoryAction.setEnabled(true);
-               } else {
-                       defaultTempDirectory.setSelected(true);
-               }
-       }
-
-       /**
-        * Returns the configuration location.
-        *
-        * @return The configuration location
-        */
-       public ConfigurationLocation getConfigurationLocation() {
-               return configurationLocation;
-       }
-
-       /**
-        * Sets the configuration location.
-        *
-        * @param configurationLocation
-        *            The configuration location
-        */
-       public void setConfigurationLocation(ConfigurationLocation configurationLocation) {
-               this.configurationLocation = configurationLocation;
-               switch (configurationLocation) {
-               case NEXT_TO_JAR_FILE:
-                       nextToJarFile.setSelected(true);
-                       break;
-               case HOME_DIRECTORY:
-                       homeDirectory.setSelected(true);
-                       break;
-               case CUSTOM:
-                       customDirectory.setSelected(true);
-                       break;
-               }
-       }
-
-       /**
-        * Sets whether it is possible to select the “next to JAR file” option for
-        * the configuration location.
-        *
-        * @param nextToJarFile
-        *            {@code true} if the configuration file can be saved next to
-        *            the JAR file, {@code false} otherwise
-        */
-       public void setHasNextToJarConfiguration(boolean nextToJarFile) {
-               this.nextToJarFile.setEnabled(nextToJarFile);
-       }
-
-       /**
-        * Sets whether it is possible to select the “custom location” option for
-        * the configuration location.
-        *
-        * @param customDirectory
-        *            {@code true} if the configuration file can be saved to a
-        *            custom location, {@code false} otherwise
-        */
-       public void setHasCustomConfiguration(boolean customDirectory) {
-               this.customDirectory.setEnabled(customDirectory);
-       }
-
-       /**
-        * Returns whether to use the “early encode“ flag for the insert.
-        *
-        * @return {@code true} to set the “early encode” flag for the insert,
-        *         {@code false} otherwise
-        */
-       public boolean useEarlyEncode() {
-               return useEarlyEncode;
-       }
-
-       /**
-        * Sets whether to use the “early encode“ flag for the insert.
-        *
-        * @param useEarlyEncode
-        *            {@code true} to set the “early encode” flag for the insert,
-        *            {@code false} otherwise
-        */
-       public void setUseEarlyEncode(boolean useEarlyEncode) {
-               useEarlyEncodeCheckBox.setSelected(useEarlyEncode);
-       }
-
-       /**
-        * Returns the configured insert priority.
-        *
-        * @return The insert priority
-        */
-       public PriorityClass getPriority() {
-               return priority;
-       }
-
-       /**
-        * Sets the insert priority.
-        *
-        * @param priority
-        *            The insert priority
-        */
-       public void setPriority(PriorityClass priority) {
-               insertPriorityComboBox.setSelectedItem(priority);
-       }
-
-       /**
-        * Returns the selected manifest putter.
-        *
-        * @return The selected manifest putter
-        */
-       public ManifestPutter getManifestPutter() {
-               return (ManifestPutter) manifestPutterComboBox.getSelectedItem();
-       }
-
-       /**
-        * Sets the manifest putter.
-        *
-        * @param manifestPutter
-        *            The manifest putter
-        */
-       public void setManifestPutter(ManifestPutter manifestPutter) {
-               manifestPutterComboBox.setSelectedItem(manifestPutter);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pageAdded(TWizard wizard) {
-               super.pageAdded(wizard);
-               this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes"));
-               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
-               this.wizard.setNextEnabled(false);
-       }
-
-       //
-       // PRIVATE METHODS
-       //
-
-       /**
-        * Initializes this page.
-        */
-       private void pageInit() {
-               createActions();
-               setLayout(new BorderLayout(12, 12));
-               add(createPreferencesPanel(), BorderLayout.CENTER);
-       }
-
-       /**
-        * Creates all actions.
-        */
-       private void createActions() {
-               selectDefaultTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.default")) {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               selectDefaultTempDirectory();
-                       }
-               };
-               selectCustomTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.custom")) {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               selectCustomTempDirectory();
-                       }
-               };
-               chooseTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.choose")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent e) {
-                               chooseTempDirectory();
-                       }
-               };
-               nextToJarFileAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.jar")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionevent) {
-                               configurationLocation = ConfigurationLocation.NEXT_TO_JAR_FILE;
-                       }
-               };
-               homeDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.home")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionevent) {
-                               configurationLocation = ConfigurationLocation.HOME_DIRECTORY;
-                       }
-               };
-               customDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.custom")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               configurationLocation = ConfigurationLocation.CUSTOM;
-                       }
-               };
-               useEarlyEncodeAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.use-early-encode")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               useEarlyEncode = useEarlyEncodeCheckBox.isSelected();
-                       }
-               };
-               priorityAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.priority")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               priority = (PriorityClass) insertPriorityComboBox.getSelectedItem();
-                       }
-               };
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               selectDefaultTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.default"));
-                               selectCustomTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.custom"));
-                               chooseTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.choose"));
-                               nextToJarFileAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.jar"));
-                               homeDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.home"));
-                               customDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.custom"));
-                               useEarlyEncodeAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.insert-options.use-early-encode"));
-                       }
-               });
-       }
-
-       /**
-        * Creates the panel containing all preferences.
-        *
-        * @return The preferences panel
-        */
-       private JPanel createPreferencesPanel() {
-               JPanel preferencesPanel = new JPanel(new GridBagLayout());
-               preferencesPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
-
-               final JLabel tempDirectoryLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.temp-directory") + "</b></html>");
-               preferencesPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
-
-               defaultTempDirectory = new JRadioButton(selectDefaultTempDirectoryAction);
-               preferencesPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0));
-
-               customTempDirectory = new JRadioButton(selectCustomTempDirectoryAction);
-               preferencesPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
-
-               ButtonGroup tempDirectoryButtonGroup = new ButtonGroup();
-               defaultTempDirectory.getModel().setGroup(tempDirectoryButtonGroup);
-               customTempDirectory.getModel().setGroup(tempDirectoryButtonGroup);
-
-               tempDirectoryTextField = new JTextField();
-               tempDirectoryTextField.setEditable(false);
-               if (tempDirectory != null) {
-                       tempDirectoryTextField.setText(tempDirectory);
-                       customTempDirectory.setSelected(true);
-               } else {
-                       defaultTempDirectory.setSelected(true);
-               }
-               chooseTempDirectoryAction.setEnabled(tempDirectory != null);
-               preferencesPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0));
-
-               JButton chooseButton = new JButton(chooseTempDirectoryAction);
-               preferencesPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0));
-
-               final JLabel configurationDirectoryLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.config-directory") + "</b></html>");
-               preferencesPanel.add(configurationDirectoryLabel, new GridBagConstraints(0, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 0, 0, 0), 0, 0));
-
-               nextToJarFile = new JRadioButton(nextToJarFileAction);
-               preferencesPanel.add(nextToJarFile, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0));
-
-               homeDirectory = new JRadioButton(homeDirectoryAction);
-               preferencesPanel.add(homeDirectory, new GridBagConstraints(0, 5, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
-
-               customDirectory = new JRadioButton(customDirectoryAction);
-               preferencesPanel.add(customDirectory, new GridBagConstraints(0, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
-
-               ButtonGroup configurationDirectoryButtonGroup = new ButtonGroup();
-               configurationDirectoryButtonGroup.add(nextToJarFile);
-               configurationDirectoryButtonGroup.add(homeDirectory);
-               configurationDirectoryButtonGroup.add(customDirectory);
-
-               final JLabel insertOptionsLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.insert-options") + "</b></html>");
-               preferencesPanel.add(insertOptionsLabel, new GridBagConstraints(0, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
-
-               useEarlyEncodeCheckBox = new JCheckBox(useEarlyEncodeAction);
-               preferencesPanel.add(useEarlyEncodeCheckBox, new GridBagConstraints(0, 8, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-
-               final JLabel insertPriorityLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.priority"));
-               preferencesPanel.add(insertPriorityLabel, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-
-               insertPriorityComboBox = new JComboBox(new PriorityClass[] { PriorityClass.MINIMUM, PriorityClass.PREFETCH, PriorityClass.BULK, PriorityClass.UPDATABLE, PriorityClass.SEMI_INTERACTIVE, PriorityClass.INTERACTIVE, PriorityClass.MAXIMUM });
-               insertPriorityComboBox.setAction(priorityAction);
-               preferencesPanel.add(insertPriorityComboBox, new GridBagConstraints(1, 9, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0));
-
-               final JLabel manifestPutterLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.manifest-putter"));
-               preferencesPanel.add(manifestPutterLabel, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-
-               manifestPutterComboBox = new JComboBox(ManifestPutter.values());
-               preferencesPanel.add(manifestPutterComboBox, new GridBagConstraints(1, 10, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       public void run() {
-                               tempDirectoryLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.temp-directory") + "</b></html>");
-                               configurationDirectoryLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.config-directory") + "</b></html>");
-                               insertOptionsLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.insert-options") + "</b></html>");
-                               insertPriorityLabel.setText(I18n.getMessage("jsite.preferences.insert-options.priority"));
-                               manifestPutterLabel.setText(I18n.getMessage("jsite.preferences.insert-options.manifest-putter"));
-                       }
-               });
-
-               return preferencesPanel;
-       }
-
-       /**
-        * Activates the default temp directory radio button.
-        */
-       private void selectDefaultTempDirectory() {
-               tempDirectoryTextField.setEnabled(false);
-               chooseTempDirectoryAction.setEnabled(false);
-               tempDirectory = null;
-       }
-
-       /**
-        * Activates the custom temp directory radio button.
-        */
-       private void selectCustomTempDirectory() {
-               tempDirectoryTextField.setEnabled(true);
-               chooseTempDirectoryAction.setEnabled(true);
-               if (tempDirectoryTextField.getText().length() == 0) {
-                       chooseTempDirectory();
-                       if (tempDirectoryTextField.getText().length() == 0) {
-                               defaultTempDirectory.setSelected(true);
-                       }
-               }
-       }
-
-       /**
-        * Lets the user choose a new temp directory.
-        */
-       private void chooseTempDirectory() {
-               JFileChooser fileChooser = new JFileChooser(tempDirectory);
-               fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
-               int returnValue = fileChooser.showDialog(wizard, I18n.getMessage("jsite.preferences.temp-directory.choose.approve"));
-               if (returnValue == JFileChooser.CANCEL_OPTION) {
-                       return;
-               }
-               tempDirectory = fileChooser.getSelectedFile().getPath();
-               tempDirectoryTextField.setText(tempDirectory);
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/de/todesbaum/jsite/gui/ProjectFilesPage.java
deleted file mode 100644 (file)
index 80bb52c..0000000
+++ /dev/null
@@ -1,577 +0,0 @@
-/*
- * jSite - ProjectFilesPage.java - Copyright © 2006–2012 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.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.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-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.JTextField;
-import javax.swing.ListSelectionModel;
-import javax.swing.SwingUtilities;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.Document;
-
-import de.todesbaum.jsite.application.FileOption;
-import de.todesbaum.jsite.application.Project;
-import de.todesbaum.jsite.gui.FileScanner.ScannedFile;
-import de.todesbaum.jsite.i18n.I18n;
-import de.todesbaum.jsite.i18n.I18nContainer;
-import de.todesbaum.util.mime.DefaultMIMETypes;
-import de.todesbaum.util.swing.TLabel;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-
-/**
- * Wizard page that lets the user manage the files of a project.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener {
-
-       /** The project. */
-       private Project project;
-
-       /** The “scan files” action. */
-       private Action scanAction;
-
-       /** The “ignore hidden files” checkbox. */
-       private JCheckBox ignoreHiddenFilesCheckBox;
-
-       /** The list of project files. */
-       private JList projectFileList;
-
-       /** The “default file” checkbox. */
-       private JCheckBox defaultFileCheckBox;
-
-       /** The “insert” checkbox. */
-       private JCheckBox fileOptionsInsertCheckBox;
-
-       /** The “force insert” checkbox. */
-       private JCheckBox fileOptionsForceInsertCheckBox;
-
-       /** The “insert redirect” checkbox. */
-       private JCheckBox fileOptionsInsertRedirectCheckBox;
-
-       /** The “custom key” textfield. */
-       private JTextField fileOptionsCustomKeyTextField;
-
-       /** The “rename” check box. */
-       private JCheckBox fileOptionsRenameCheckBox;
-
-       /** The “new name” text field. */
-       private JTextField fileOptionsRenameTextField;
-
-       /** The “mime type” combo box. */
-       private JComboBox fileOptionsMIMETypeComboBox;
-
-       /**
-        * Creates a new project file page.
-        *
-        * @param wizard
-        *            The wizard the page belongs to
-        */
-       public ProjectFilesPage(final TWizard wizard) {
-               super(wizard);
-               pageInit();
-       }
-
-       /**
-        * Initializes the page and all its actions and components.
-        */
-       private void pageInit() {
-               createActions();
-               setLayout(new BorderLayout(12, 12));
-               add(createProjectFilesPanel(), BorderLayout.CENTER);
-       }
-
-       /**
-        * Creates all actions.
-        */
-       private void createActions() {
-               scanAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.rescan")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       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"));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               scanAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.rescan"));
-                               scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip"));
-                       }
-               });
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pageAdded(TWizard wizard) {
-               actionScan();
-               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
-               this.wizard.setNextName(I18n.getMessage("jsite.project-files.insert-now"));
-               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
-       }
-
-       /**
-        * Creates the panel contains the project file list and options.
-        *
-        * @return The created panel
-        */
-       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);
-
-               ignoreHiddenFilesCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.ignore-hidden-files"));
-               ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.ignore-hidden-files.tooltip"));
-               ignoreHiddenFilesCheckBox.setName("ignore-hidden-files");
-               ignoreHiddenFilesCheckBox.addActionListener(this);
-               fileOptionsPanel.add(ignoreHiddenFilesCheckBox, 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 JButton(scanAction), new GridBagConstraints(0, 1, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0));
-
-               final JLabel fileOptionsLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project-files.file-options") + "</b></html>");
-               fileOptionsPanel.add(fileOptionsLabel, new GridBagConstraints(0, 2, 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, 3, 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, 4, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-
-               fileOptionsForceInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.force-insert"));
-               fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip"));
-               fileOptionsForceInsertCheckBox.setName("force-insert");
-               fileOptionsForceInsertCheckBox.setMnemonic(KeyEvent.VK_F);
-               fileOptionsForceInsertCheckBox.addActionListener(this);
-               fileOptionsForceInsertCheckBox.setEnabled(false);
-
-               fileOptionsPanel.add(fileOptionsForceInsertCheckBox, new GridBagConstraints(0, 5, 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);
-
-               fileOptionsInsertRedirectCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert-redirect"), false);
-               fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip"));
-               fileOptionsInsertRedirectCheckBox.setName("insert-redirect");
-               fileOptionsInsertRedirectCheckBox.setMnemonic(KeyEvent.VK_R);
-               fileOptionsInsertRedirectCheckBox.addActionListener(this);
-               fileOptionsInsertRedirectCheckBox.setEnabled(false);
-
-               final TLabel customKeyLabel = new TLabel(I18n.getMessage("jsite.project-files.custom-key") + ":", KeyEvent.VK_K, fileOptionsCustomKeyTextField);
-               fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, 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(customKeyLabel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));
-               fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
-
-               fileOptionsRenameCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.rename"), false);
-               fileOptionsRenameCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.rename.tooltip"));
-               fileOptionsRenameCheckBox.setName("rename");
-               fileOptionsRenameCheckBox.setMnemonic(KeyEvent.VK_N);
-               fileOptionsRenameCheckBox.addActionListener(this);
-               fileOptionsRenameCheckBox.setEnabled(false);
-
-               fileOptionsRenameTextField = new JTextField();
-               fileOptionsRenameTextField.setEnabled(false);
-               fileOptionsRenameTextField.getDocument().addDocumentListener(new DocumentListener() {
-
-                       @SuppressWarnings("synthetic-access")
-                       private void storeText(DocumentEvent documentEvent) {
-                               FileOption fileOption = getSelectedFile();
-                               if (fileOption == null) {
-                                       /* no file selected. */
-                                       return;
-                               }
-                               Document document = documentEvent.getDocument();
-                               int documentLength = document.getLength();
-                               try {
-                                       fileOption.setChangedName(document.getText(0, documentLength).trim());
-                               } catch (BadLocationException ble1) {
-                                       /* ignore, it should never happen. */
-                               }
-                       }
-
-                       public void changedUpdate(DocumentEvent documentEvent) {
-                               storeText(documentEvent);
-                       }
-
-                       public void insertUpdate(DocumentEvent documentEvent) {
-                               storeText(documentEvent);
-                       }
-
-                       public void removeUpdate(DocumentEvent documentEvent) {
-                               storeText(documentEvent);
-                       }
-
-               });
-
-               fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
-               fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 7, 3, 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.setEditable(true);
-               fileOptionsMIMETypeComboBox.setEnabled(false);
-
-               final TLabel mimeTypeLabel = new TLabel(I18n.getMessage("jsite.project-files.mime-type") + ":", KeyEvent.VK_M, fileOptionsMIMETypeComboBox);
-               fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 8, 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, 8, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               ignoreHiddenFilesCheckBox.setText(I18n.getMessage("jsite.project-files.ignore-hidden-files"));
-                               ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.projet-files.ignore-hidden-files.tooltip"));
-                               fileOptionsLabel.setText("<html><b>" + I18n.getMessage("jsite.project-files.file-options") + "</b></html>");
-                               defaultFileCheckBox.setText(I18n.getMessage("jsite.project-files.default"));
-                               defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip"));
-                               fileOptionsInsertCheckBox.setText(I18n.getMessage("jsite.project-files.insert"));
-                               fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip"));
-                               fileOptionsForceInsertCheckBox.setText(I18n.getMessage("jsite.project-files.force-insert"));
-                               fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip"));
-                               fileOptionsInsertRedirectCheckBox.setText(I18n.getMessage("jsite.project-files.insert-redirect"));
-                               fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip"));
-                               fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip"));
-                               customKeyLabel.setText(I18n.getMessage("jsite.project-files.custom-key") + ":");
-                               fileOptionsRenameCheckBox.setText("jsite.project-files.rename");
-                               fileOptionsRenameCheckBox.setToolTipText("jsite.project-files.rename.tooltip");
-                               fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip"));
-                               mimeTypeLabel.setText(I18n.getMessage("jsite.project-files.mime-type") + ":");
-                       }
-               });
-
-               return projectFilesPanel;
-       }
-
-       /**
-        * Sets the project whose files to manage.
-        *
-        * @param project
-        *            The project whose files to manage
-        */
-       public void setProject(final Project project) {
-               this.project = project;
-               setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName()));
-               setDescription(I18n.getMessage("jsite.project-files.description"));
-               ignoreHiddenFilesCheckBox.setSelected(project.isIgnoreHiddenFiles());
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName()));
-                               setDescription(I18n.getMessage("jsite.project-files.description"));
-                       }
-               });
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Rescans the project’s files.
-        */
-       private 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();
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * Updates the file list.
-        */
-       public void fileScannerFinished(FileScanner fileScanner) {
-               final boolean error = fileScanner.isError();
-               if (!error) {
-                       final List<ScannedFile> files = fileScanner.getFiles();
-                       SwingUtilities.invokeLater(new Runnable() {
-
-                               @SuppressWarnings("synthetic-access")
-                               public void run() {
-                                       projectFileList.setListData(files.toArray());
-                                       projectFileList.clearSelection();
-                               }
-                       });
-                       Set<String> entriesToRemove = new HashSet<String>();
-                       Iterator<String> filenames = new HashSet<String>(project.getFileOptions().keySet()).iterator();
-                       while (filenames.hasNext()) {
-                               String filename = filenames.next();
-                               boolean found = false;
-                               for (ScannedFile scannedFile : files) {
-                                       if (scannedFile.getFilename().equals(filename)) {
-                                               found = true;
-                                               break;
-                                       }
-                               }
-                               if (!found) {
-                                       entriesToRemove.add(filename);
-                               }
-                       }
-                       for (String filename : entriesToRemove) {
-                               project.setFileOption(filename, null);
-                       }
-               } else {
-                       JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.scan-error"), null, JOptionPane.ERROR_MESSAGE);
-               }
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               wizard.setPreviousEnabled(true);
-                               wizard.setNextEnabled(!error);
-                               wizard.setQuitEnabled(true);
-                       }
-               });
-       }
-
-       /**
-        * Returns the {@link FileOption file options} for the currently selected
-        * file.
-        *
-        * @return The {@link FileOption}s for the selected file, or {@code null} if
-        *         no file is selected
-        */
-       private FileOption getSelectedFile() {
-               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
-               if (scannedFile == null) {
-                       return null;
-               }
-               return project.getFileOption(scannedFile.getFilename());
-       }
-
-       //
-       // INTERFACE ActionListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void actionPerformed(ActionEvent actionEvent) {
-               Object source = actionEvent.getSource();
-               if ((source instanceof JCheckBox) && ("ignore-hidden-files".equals(((JCheckBox) source).getName()))) {
-                       project.setIgnoreHiddenFiles(((JCheckBox) source).isSelected());
-                       actionScan();
-                       return;
-               }
-               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
-               if (scannedFile == null) {
-                       return;
-               }
-               String filename = scannedFile.getFilename();
-               FileOption fileOption = project.getFileOption(filename);
-               if (source instanceof JCheckBox) {
-                       JCheckBox checkBox = (JCheckBox) source;
-                       if ("default-file".equals(checkBox.getName())) {
-                               if (checkBox.isSelected()) {
-                                       if (filename.indexOf('/') > -1) {
-                                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.invalid-default-file"), null, JOptionPane.ERROR_MESSAGE);
-                                               checkBox.setSelected(false);
-                                       } else {
-                                               project.setIndexFile(filename);
-                                       }
-                               } else {
-                                       if (filename.equals(project.getIndexFile())) {
-                                               project.setIndexFile(null);
-                                       }
-                               }
-                       } else if ("insert".equals(checkBox.getName())) {
-                               boolean isInsert = checkBox.isSelected();
-                               fileOption.setInsert(isInsert);
-                               fileOptionsInsertRedirectCheckBox.setEnabled(!isInsert);
-                       } else if ("force-insert".equals(checkBox.getName())) {
-                               boolean isForceInsert = checkBox.isSelected();
-                               fileOption.setForceInsert(isForceInsert);
-                       } else if ("insert-redirect".equals(checkBox.getName())) {
-                               boolean isInsertRedirect = checkBox.isSelected();
-                               fileOption.setInsertRedirect(isInsertRedirect);
-                               fileOptionsCustomKeyTextField.setEnabled(isInsertRedirect);
-                       } else if ("rename".equals(checkBox.getName())) {
-                               boolean isRenamed = checkBox.isSelected();
-                               fileOptionsRenameTextField.setEnabled(isRenamed);
-                               fileOption.setChangedName(isRenamed ? fileOptionsRenameTextField.getText() : "");
-                       }
-               } else if (source instanceof JComboBox) {
-                       JComboBox comboBox = (JComboBox) source;
-                       if ("project-files.mime-type".equals(comboBox.getName())) {
-                               fileOption.setMimeType((String) comboBox.getSelectedItem());
-                       }
-               }
-       }
-
-       //
-       // INTERFACE ListSelectionListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @SuppressWarnings("null")
-       public void valueChanged(ListSelectionEvent e) {
-               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
-               boolean enabled = scannedFile != null;
-               String filename = (scannedFile == null) ? null : scannedFile.getFilename();
-               defaultFileCheckBox.setEnabled(enabled);
-               fileOptionsInsertCheckBox.setEnabled(enabled);
-               fileOptionsRenameCheckBox.setEnabled(enabled);
-               fileOptionsMIMETypeComboBox.setEnabled(enabled);
-               if (filename != null) {
-                       FileOption fileOption = project.getFileOption(filename);
-                       defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile()));
-                       fileOptionsInsertCheckBox.setSelected(fileOption.isInsert());
-                       fileOptionsForceInsertCheckBox.setEnabled(scannedFile.getHash().equals(fileOption.getLastInsertHash()));
-                       fileOptionsForceInsertCheckBox.setSelected(fileOption.isForceInsert());
-                       fileOptionsInsertRedirectCheckBox.setEnabled(!fileOption.isInsert());
-                       fileOptionsInsertRedirectCheckBox.setSelected(fileOption.isInsertRedirect());
-                       fileOptionsCustomKeyTextField.setEnabled(fileOption.isInsertRedirect());
-                       fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey());
-                       fileOptionsRenameCheckBox.setSelected(fileOption.hasChangedName());
-                       fileOptionsRenameTextField.setEnabled(fileOption.hasChangedName());
-                       fileOptionsRenameTextField.setText(fileOption.getChangedName());
-                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType());
-               } else {
-                       defaultFileCheckBox.setSelected(false);
-                       fileOptionsInsertCheckBox.setSelected(true);
-                       fileOptionsForceInsertCheckBox.setEnabled(false);
-                       fileOptionsForceInsertCheckBox.setSelected(false);
-                       fileOptionsInsertRedirectCheckBox.setEnabled(false);
-                       fileOptionsInsertRedirectCheckBox.setSelected(false);
-                       fileOptionsCustomKeyTextField.setEnabled(false);
-                       fileOptionsCustomKeyTextField.setText("CHK@");
-                       fileOptionsRenameCheckBox.setEnabled(false);
-                       fileOptionsRenameCheckBox.setSelected(false);
-                       fileOptionsRenameTextField.setEnabled(false);
-                       fileOptionsRenameTextField.setText("");
-                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE);
-               }
-       }
-
-       //
-       // INTERFACE DocumentListener
-       //
-
-       /**
-        * Updates the options of the currently selected file with the changes made
-        * in the “custom key” textfield.
-        *
-        * @param documentEvent
-        *            The document event to process
-        */
-       private void processDocumentUpdate(DocumentEvent documentEvent) {
-               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
-               if (scannedFile == null) {
-                       return;
-               }
-               FileOption fileOption = project.getFileOption(scannedFile.getFilename());
-               Document document = documentEvent.getDocument();
-               try {
-                       String text = document.getText(0, document.getLength());
-                       fileOption.setCustomKey(text);
-               } catch (BadLocationException ble1) {
-                       /* ignore. */
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void changedUpdate(DocumentEvent documentEvent) {
-               processDocumentUpdate(documentEvent);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void insertUpdate(DocumentEvent documentEvent) {
-               processDocumentUpdate(documentEvent);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void removeUpdate(DocumentEvent documentEvent) {
-               processDocumentUpdate(documentEvent);
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/de/todesbaum/jsite/gui/ProjectInsertPage.java
deleted file mode 100644 (file)
index bfd8b89..0000000
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * jSite - ProjectInsertPage.java - Copyright © 2006–2012 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.Font;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-import java.awt.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.ClipboardOwner;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.awt.event.ActionEvent;
-import java.awt.event.KeyEvent;
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.util.Date;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.JButton;
-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.AbortedException;
-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.jsite.i18n.I18nContainer;
-import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
-import de.todesbaum.util.freenet.fcp2.PriorityClass;
-import de.todesbaum.util.io.StreamCopier.ProgressListener;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-
-/**
- * Wizard page that shows the progress of an insert.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ProjectInsertPage extends TWizardPage implements InsertListener, ClipboardOwner {
-
-       /** The logger. */
-       private static final Logger logger = Logger.getLogger(ProjectInsertPage.class.getName());
-
-       /** The project inserter. */
-       private ProjectInserter projectInserter;
-
-       /** The “copy URI” action. */
-       private Action copyURIAction;
-
-       /** The “request URI” textfield. */
-       private JTextField requestURITextField;
-
-       /** The “start time” label. */
-       private JLabel startTimeLabel;
-
-       /** The progress bar. */
-       private JProgressBar progressBar;
-
-       /** The start time of the insert. */
-       private long startTime = 0;
-
-       /** The number of inserted blocks. */
-       private volatile int insertedBlocks;
-
-       /** Whether the “copy URI to clipboard” button was used. */
-       private boolean uriCopied;
-
-       /** Whether the insert is currently running. */
-       private volatile boolean running = false;
-
-       /**
-        * Creates a new progress insert wizard page.
-        *
-        * @param wizard
-        *            The wizard this page belongs to
-        */
-       public ProjectInsertPage(final TWizard wizard) {
-               super(wizard);
-               createActions();
-               pageInit();
-               setHeading(I18n.getMessage("jsite.insert.heading"));
-               setDescription(I18n.getMessage("jsite.insert.description"));
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               setHeading(I18n.getMessage("jsite.insert.heading"));
-                               setDescription(I18n.getMessage("jsite.insert.description"));
-                       }
-               });
-               projectInserter = new ProjectInserter();
-               projectInserter.addInsertListener(this);
-       }
-
-       /**
-        * Creates all used actions.
-        */
-       private void createActions() {
-               copyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionCopyURI();
-                       }
-               };
-               copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
-               copyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U);
-               copyURIAction.setEnabled(false);
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               copyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri"));
-                               copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
-                       }
-               });
-       }
-
-       /**
-        * Initializes the page.
-        */
-       private void pageInit() {
-               setLayout(new BorderLayout(12, 12));
-               add(createProjectInsertPanel(), BorderLayout.CENTER);
-       }
-
-       /**
-        * Creates the main panel.
-        *
-        * @return The main panel
-        */
-       private JComponent createProjectInsertPanel() {
-               JComponent projectInsertPanel = new JPanel(new GridBagLayout());
-
-               requestURITextField = new JTextField();
-               requestURITextField.setEditable(false);
-
-               startTimeLabel = new JLabel();
-
-               progressBar = new JProgressBar(0, 1);
-               progressBar.setStringPainted(true);
-               progressBar.setValue(0);
-
-               final JLabel projectInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.insert.project-information") + "</b></html>");
-               projectInsertPanel.add(projectInformationLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
-               final JLabel requestURILabel = new JLabel(I18n.getMessage("jsite.insert.request-uri") + ":");
-               projectInsertPanel.add(requestURILabel, 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));
-               final JLabel startTimeLeftLabel = new JLabel(I18n.getMessage("jsite.insert.start-time") + ":");
-               projectInsertPanel.add(startTimeLeftLabel, 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));
-               final JLabel progressLabel = new JLabel(I18n.getMessage("jsite.insert.progress") + ":");
-               projectInsertPanel.add(progressLabel, 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));
-               projectInsertPanel.add(new JButton(copyURIAction), new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               projectInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.insert.project-information") + "</b></html>");
-                               requestURILabel.setText(I18n.getMessage("jsite.insert.request-uri") + ":");
-                               startTimeLeftLabel.setText(I18n.getMessage("jsite.insert.start-time") + ":");
-                               if (startTime != 0) {
-                                       startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date(startTime)));
-                               } else {
-                                       startTimeLabel.setText("");
-                               }
-                               progressLabel.setText(I18n.getMessage("jsite.insert.progress") + ":");
-                       }
-               });
-
-               return projectInsertPanel;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pageAdded(TWizard wizard) {
-               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
-               this.wizard.setPreviousEnabled(false);
-               this.wizard.setNextName(I18n.getMessage("jsite.general.cancel"));
-               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
-       }
-
-       /**
-        * Starts the insert.
-        */
-       public void startInsert() {
-               running = true;
-               copyURIAction.setEnabled(false);
-               progressBar.setValue(0);
-               progressBar.setString(I18n.getMessage("jsite.insert.starting"));
-               progressBar.setFont(progressBar.getFont().deriveFont(Font.PLAIN));
-               projectInserter.start(new ProgressListener() {
-
-                       public void onProgress(final long copied, final long length) {
-                               SwingUtilities.invokeLater(new Runnable() {
-
-                                       /**
-                                        * {@inheritDoc}
-                                        */
-                                       @SuppressWarnings("synthetic-access")
-                                       public void run() {
-                                               int divisor = 1;
-                                               while (((copied / divisor) > Integer.MAX_VALUE) || ((length / divisor) > Integer.MAX_VALUE)) {
-                                                       divisor *= 10;
-                                               }
-                                               progressBar.setMaximum((int) (length / divisor));
-                                               progressBar.setValue((int) (copied / divisor));
-                                               progressBar.setString("Uploaded: " + copied + " / " + length);
-                                       }
-                               });
-                       }
-               });
-       }
-
-       /**
-        * Stops the currently running insert.
-        */
-       public void stopInsert() {
-               if (running) {
-                       wizard.setNextEnabled(false);
-                       projectInserter.stop();
-               }
-       }
-
-       /**
-        * Returns whether the insert is currently running.
-        *
-        * @return {@code true} if the insert is currently running, {@code false}
-        *         otherwise
-        */
-       public boolean isRunning() {
-               return running;
-       }
-
-       /**
-        * Sets the project to insert.
-        *
-        * @param project
-        *            The project to insert
-        */
-       public void setProject(final Project project) {
-               projectInserter.setProject(project);
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               requestURITextField.setText(project.getFinalRequestURI(1));
-                       }
-               });
-       }
-
-       /**
-        * Sets the freenet interface to use.
-        *
-        * @param freenetInterface
-        *            The freenet interface to use
-        */
-       public void setFreenetInterface(Freenet7Interface freenetInterface) {
-               projectInserter.setFreenetInterface(freenetInterface);
-       }
-
-       /**
-        * Sets the project inserter’s temp directory.
-        *
-        * @see ProjectInserter#setTempDirectory(String)
-        * @param tempDirectory
-        *            The temp directory to use, or {@code null} to use the system
-        *            default
-        */
-       public void setTempDirectory(String tempDirectory) {
-               projectInserter.setTempDirectory(tempDirectory);
-       }
-
-       /**
-        * Returns whether the “copy URI to clipboard” button was used.
-        *
-        * @return {@code true} if an URI was copied to clipboard, {@code false}
-        *         otherwise
-        */
-       public boolean wasUriCopied() {
-               return uriCopied;
-       }
-
-       /**
-        * Sets whether to use the “early encode“ flag for the insert.
-        *
-        * @param useEarlyEncode
-        *            {@code true} to set the “early encode” flag for the insert,
-        *            {@code false} otherwise
-        */
-       public void setUseEarlyEncode(boolean useEarlyEncode) {
-               projectInserter.setUseEarlyEncode(useEarlyEncode);
-       }
-
-       /**
-        * Sets the insert priority.
-        *
-        * @param priority
-        *            The insert priority
-        */
-       public void setPriority(PriorityClass priority) {
-               projectInserter.setPriority(priority);
-       }
-
-       /**
-        * Sets the manifest putter to use for the insert.
-        *
-        * @see ProjectInserter#setManifestPutter(ManifestPutter)
-        * @param manifestPutter
-        *            The manifest putter
-        */
-       public void setManifestPutter(ManifestPutter manifestPutter) {
-               projectInserter.setManifestPutter(manifestPutter);
-       }
-
-       //
-       // INTERFACE InsertListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectInsertStarted(final Project project) {
-
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date()));
-                       }
-               });
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectUploadFinished(Project project) {
-               startTime = System.currentTimeMillis();
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               progressBar.setString(I18n.getMessage("jsite.insert.starting"));
-                               progressBar.setValue(0);
-                       }
-               });
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectURIGenerated(Project project, final String uri) {
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               copyURIAction.setEnabled(true);
-                               requestURITextField.setText(uri);
-                       }
-               });
-               logger.log(Level.FINEST, "Insert generated URI: " + uri);
-               int slash = uri.indexOf('/');
-               slash = uri.indexOf('/', slash + 1);
-               int secondSlash = uri.indexOf('/', slash + 1);
-               if (secondSlash == -1) {
-                       secondSlash = uri.length();
-               }
-               String editionNumber = uri.substring(slash + 1, secondSlash);
-               logger.log(Level.FINEST, "Extracted edition number: " + editionNumber);
-               int edition = -1;
-               try {
-                       edition = Integer.valueOf(editionNumber);
-               } catch (NumberFormatException nfe1) {
-                       /* ignore. */
-               }
-               logger.log(Level.FINEST, "Insert edition: " + edition + ", Project edition: " + project.getEdition());
-               if ((edition != -1) && (edition == project.getEdition())) {
-                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.reinserted-edition"), I18n.getMessage("jsite.insert.reinserted-edition.title"), JOptionPane.INFORMATION_MESSAGE);
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectInsertProgress(Project project, final int succeeded, final int failed, final int fatal, final int total, final boolean finalized) {
-               insertedBlocks = succeeded;
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               if (total == 0) {
-                                       return;
-                               }
-                               progressBar.setMaximum(total);
-                               progressBar.setValue(succeeded + failed + fatal);
-                               int progress = (succeeded + failed + fatal) * 100 / total;
-                               StringBuilder progressString = new StringBuilder();
-                               progressString.append(progress).append("% (");
-                               progressString.append(succeeded + failed + fatal).append('/').append(total);
-                               progressString.append(") (");
-                               progressString.append(getTransferRate());
-                               progressString.append(' ').append(I18n.getMessage("jsite.insert.k-per-s")).append(')');
-                               progressBar.setString(progressString.toString());
-                               if (finalized) {
-                                       progressBar.setFont(progressBar.getFont().deriveFont(Font.BOLD));
-                               }
-                       }
-               });
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectInsertFinished(Project project, boolean success, Throwable cause) {
-               running = false;
-               if (success) {
-                       String copyURILabel = I18n.getMessage("jsite.insert.okay-copy-uri");
-                       int selectedValue = JOptionPane.showOptionDialog(this, I18n.getMessage("jsite.insert.inserted"), I18n.getMessage("jsite.insert.done.title"), 0, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { I18n.getMessage("jsite.general.ok"), copyURILabel }, copyURILabel);
-                       if (selectedValue == 1) {
-                               actionCopyURI();
-                       }
-               } else {
-                       if (cause == null) {
-                               JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-failed"), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE);
-                       } else {
-                               if (cause instanceof AbortedException) {
-                                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-aborted"), I18n.getMessage("jsite.insert.insert-aborted.title"), JOptionPane.INFORMATION_MESSAGE);
-                               } else {
-                                       JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.insert.insert-failed-with-cause"), cause.getMessage()), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE);
-                               }
-                       }
-               }
-               SwingUtilities.invokeLater(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               progressBar.setValue(progressBar.getMaximum());
-                               progressBar.setString(I18n.getMessage("jsite.insert.done") + " (" + getTransferRate() + " " + I18n.getMessage("jsite.insert.k-per-s") + ")");
-                               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-                               wizard.setNextEnabled(true);
-                               wizard.setQuitEnabled(true);
-                       }
-               });
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Copies the request URI of the project to the clipboard.
-        */
-       private void actionCopyURI() {
-               uriCopied = true;
-               Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
-               clipboard.setContents(new StringSelection(requestURITextField.getText()), this);
-       }
-
-       /**
-        * Formats the given number so that it always has the the given number of
-        * fractional digits.
-        *
-        * @param number
-        *            The number to format
-        * @param digits
-        *            The number of fractional digits
-        * @return The formatted number
-        */
-       private String formatNumber(double number, int digits) {
-               int multiplier = (int) Math.pow(10, digits);
-               String formattedNumber = String.valueOf((int) (number * multiplier) / (double) multiplier);
-               if (formattedNumber.indexOf('.') == -1) {
-                       formattedNumber += '.';
-                       for (int digit = 0; digit < digits; digit++) {
-                               formattedNumber += "0";
-                       }
-               }
-               return formattedNumber;
-       }
-
-       /**
-        * Returns the formatted transfer rate at this point.
-        *
-        * @return The formatted transfer rate
-        */
-       private String getTransferRate() {
-               return formatNumber(insertedBlocks * 32.0 / ((System.currentTimeMillis() - startTime) / 1000), 1);
-       }
-
-       //
-       // INTERFACE ClipboardOwner
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void lostOwnership(Clipboard clipboard, Transferable contents) {
-               /* ignore. */
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/gui/ProjectPage.java b/src/de/todesbaum/jsite/gui/ProjectPage.java
deleted file mode 100644 (file)
index 98ad847..0000000
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * jSite - ProjectPage.java - Copyright © 2006–2012 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.Toolkit;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.ClipboardOwner;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-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.JTextField;
-import javax.swing.ListSelectionModel;
-import javax.swing.border.EmptyBorder;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import javax.swing.text.AbstractDocument;
-import javax.swing.text.AttributeSet;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.Document;
-import javax.swing.text.DocumentFilter;
-
-import de.todesbaum.jsite.application.Freenet7Interface;
-import de.todesbaum.jsite.application.KeyDialog;
-import de.todesbaum.jsite.application.Project;
-import de.todesbaum.jsite.i18n.I18n;
-import de.todesbaum.jsite.i18n.I18nContainer;
-import de.todesbaum.util.swing.SortedListModel;
-import de.todesbaum.util.swing.TLabel;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-
-/**
- * Wizard page that lets the user manage his projects and start inserts.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ProjectPage extends TWizardPage implements ListSelectionListener, DocumentListener, ClipboardOwner {
-
-       /** The freenet interface. */
-       private Freenet7Interface freenetInterface;
-
-       /** The “browse” action. */
-       private Action projectLocalPathBrowseAction;
-
-       /** The “add project” action. */
-       private Action projectAddAction;
-
-       /** The “delete project” action. */
-       private Action projectDeleteAction;
-
-       /** The “clone project” action. */
-       private Action projectCloneAction;
-
-       /** The “manage keys” action. */
-       private Action projectManageKeysAction;
-
-       /** The “copy URI” action. */
-       private Action projectCopyURIAction;
-
-       /** The “reset edition” action. */
-       private Action projectResetEditionAction;
-
-       /** The file chooser. */
-       private JFileChooser pathChooser;
-
-       /** The project list model. */
-       private SortedListModel<Project> projectListModel;
-
-       /** The project list scroll pane. */
-       private JScrollPane projectScrollPane;
-
-       /** The project list. */
-       private JList projectList;
-
-       /** The project name textfield. */
-       private JTextField projectNameTextField;
-
-       /** The project description textfield. */
-       private JTextField projectDescriptionTextField;
-
-       /** The local path textfield. */
-       private JTextField projectLocalPathTextField;
-
-       /** The textfield for the complete URI. */
-       private JTextField projectCompleteUriTextField;
-
-       /** The project path textfield. */
-       private JTextField projectPathTextField;
-
-       /** Whether the “copy URI to clipboard” action was used. */
-       private boolean uriCopied;
-
-       /**
-        * Creates a new project page.
-        *
-        * @param wizard
-        *            The wizard this page belongs to
-        */
-       public ProjectPage(final TWizard wizard) {
-               super(wizard);
-               setLayout(new BorderLayout(12, 12));
-               dialogInit();
-               setHeading(I18n.getMessage("jsite.project.heading"));
-               setDescription(I18n.getMessage("jsite.project.description"));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               setHeading(I18n.getMessage("jsite.project.heading"));
-                               setDescription(I18n.getMessage("jsite.project.description"));
-                       }
-               });
-       }
-
-       /**
-        * Initializes the page.
-        */
-       private void dialogInit() {
-               createActions();
-
-               pathChooser = new JFileChooser();
-               projectListModel = new SortedListModel<Project>();
-               projectList = new JList(projectListModel);
-               projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-               projectList.addListSelectionListener(this);
-
-               add(projectScrollPane = new JScrollPane(projectList), BorderLayout.LINE_START);
-               projectScrollPane.setPreferredSize(new Dimension(150, projectList.getPreferredSize().height));
-               add(createInformationPanel(), BorderLayout.CENTER);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void pageAdded(TWizard wizard) {
-               super.pageAdded(wizard);
-               projectList.clearSelection();
-               this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes"));
-               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
-               this.wizard.setNextEnabled(false);
-       }
-
-       /**
-        * Adds the given listener to the list of listeners.
-        *
-        * @param listener
-        *            The listener to add
-        */
-       public void addListSelectionListener(ListSelectionListener listener) {
-               projectList.addListSelectionListener(listener);
-       }
-
-       /**
-        * Removes the given listener from the list of listeners.
-        *
-        * @param listener
-        *            The listener to remove
-        */
-       public void removeListSelectionListener(ListSelectionListener listener) {
-               projectList.removeListSelectionListener(listener);
-       }
-
-       /**
-        * Creates all actions.
-        */
-       private void createActions() {
-               projectLocalPathBrowseAction = new AbstractAction(I18n.getMessage("jsite.project.action.browse")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       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")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       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")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       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")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       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);
-
-               projectCopyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionCopyURI();
-                       }
-               };
-               projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
-               projectCopyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U);
-               projectCopyURIAction.setEnabled(false);
-
-               projectManageKeysAction = new AbstractAction(I18n.getMessage("jsite.project.action.manage-keys")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionManageKeys();
-                       }
-               };
-               projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip"));
-               projectManageKeysAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_M);
-               projectManageKeysAction.setEnabled(false);
-
-               projectResetEditionAction = new AbstractAction(I18n.getMessage("jsite.project.action.reset-edition")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               actionResetEdition();
-                       }
-               };
-               projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip"));
-               projectResetEditionAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);
-               projectResetEditionAction.setEnabled(false);
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               projectLocalPathBrowseAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.browse"));
-                               projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip"));
-                               projectAddAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.add-project"));
-                               projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip"));
-                               projectDeleteAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.delete-project"));
-                               projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip"));
-                               projectCloneAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.clone-project"));
-                               projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip"));
-                               projectCopyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri"));
-                               projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
-                               projectManageKeysAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.manage-keys"));
-                               projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip"));
-                               projectResetEditionAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.reset-edition"));
-                               projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip"));
-                               pathChooser.setApproveButtonText(I18n.getMessage("jsite.project.action.browse.choose"));
-                       }
-               });
-       }
-
-       /**
-        * Creates the information panel.
-        *
-        * @return The information panel
-        */
-       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));
-               functionButtons.add(new JButton(projectManageKeysAction));
-
-               informationPanel.add(functionButtons, BorderLayout.PAGE_START);
-               informationPanel.add(informationTable, BorderLayout.CENTER);
-
-               final JLabel projectInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.information") + "</b></html>");
-               informationTable.add(projectInformationLabel, 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);
-
-               final TLabel projectNameLabel = new TLabel(I18n.getMessage("jsite.project.project.name") + ":", KeyEvent.VK_N, projectNameTextField);
-               informationTable.add(projectNameLabel, 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);
-
-               final TLabel projectDescriptionLabel = new TLabel(I18n.getMessage("jsite.project.project.description") + ":", KeyEvent.VK_D, projectDescriptionTextField);
-               informationTable.add(projectDescriptionLabel, 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);
-
-               final TLabel projectLocalPathLabel = new TLabel(I18n.getMessage("jsite.project.project.local-path") + ":", KeyEvent.VK_L, projectLocalPathTextField);
-               informationTable.add(projectLocalPathLabel, 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));
-
-               final JLabel projectAddressLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.address") + "</b></html>");
-               informationTable.add(projectAddressLabel, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
-
-               projectPathTextField = new JTextField();
-               projectPathTextField.getDocument().putProperty("name", "project.path");
-               projectPathTextField.getDocument().addDocumentListener(this);
-               ((AbstractDocument) projectPathTextField.getDocument()).setDocumentFilter(new DocumentFilter() {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
-                               super.insertString(fb, offset, string.replaceAll("/", ""), attr);
-                               updateCompleteURI();
-                       }
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
-                               super.replace(fb, offset, length, text.replaceAll("/", ""), attrs);
-                               updateCompleteURI();
-                       }
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @Override
-                       @SuppressWarnings("synthetic-access")
-                       public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
-                               super.remove(fb, offset, length);
-                               updateCompleteURI();
-                       }
-               });
-               projectPathTextField.setEnabled(false);
-
-               final TLabel projectPathLabel = new TLabel(I18n.getMessage("jsite.project.project.path") + ":", KeyEvent.VK_P, projectPathTextField);
-               informationTable.add(projectPathLabel, 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(projectPathTextField, new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
-
-               projectCompleteUriTextField = new JTextField();
-               projectCompleteUriTextField.setEditable(false);
-               final TLabel projectUriLabel = new TLabel(I18n.getMessage("jsite.project.project.uri") + ":", KeyEvent.VK_U, projectCompleteUriTextField);
-               informationTable.add(projectUriLabel, 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(projectCompleteUriTextField, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
-               informationTable.add(new JButton(projectCopyURIAction), new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       public void run() {
-                               projectInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.project.project.information") + "</b></html>");
-                               projectNameLabel.setText(I18n.getMessage("jsite.project.project.name") + ":");
-                               projectDescriptionLabel.setText(I18n.getMessage("jsite.project.project.description") + ":");
-                               projectLocalPathLabel.setText(I18n.getMessage("jsite.project.project.local-path") + ":");
-                               projectAddressLabel.setText("<html><b>" + I18n.getMessage("jsite.project.project.address") + "</b></html>");
-                               projectPathLabel.setText(I18n.getMessage("jsite.project.project.path") + ":");
-                               projectUriLabel.setText(I18n.getMessage("jsite.project.project.uri") + ":");
-                       }
-               });
-
-               return informationPanel;
-       }
-
-       /**
-        * Sets the project list.
-        *
-        * @param projects
-        *            The list of projects
-        */
-       public void setProjects(Project[] projects) {
-               projectListModel.clear();
-               for (Project project : projects) {
-                       projectListModel.add(project);
-               }
-       }
-
-       /**
-        * Returns the list of projects.
-        *
-        * @return The list of projects
-        */
-       public Project[] getProjects() {
-               return projectListModel.toArray(new Project[projectListModel.size()]);
-       }
-
-       /**
-        * Sets the freenet interface to use.
-        *
-        * @param freenetInterface
-        *            The freenetInterface to use
-        */
-       public void setFreenetInterface(Freenet7Interface freenetInterface) {
-               this.freenetInterface = freenetInterface;
-       }
-
-       /**
-        * Returns the currently selected project.
-        *
-        * @return The currently selected project
-        */
-       public Project getSelectedProject() {
-               return (Project) projectList.getSelectedValue();
-       }
-
-       /**
-        * Returns whether the “copy URI to clipboard” button was used.
-        *
-        * @return {@code true} if the “copy URI to clipboard” button was used,
-        *         {@code false} otherwise
-        */
-       public boolean wasUriCopied() {
-               return uriCopied;
-       }
-
-       /**
-        * Updates the currently selected project with changed information from a
-        * textfield.
-        *
-        * @param documentEvent
-        *            The document event to process
-        */
-       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) {
-                       /* ignore. */
-               }
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Lets the user choose a local path for a project.
-        */
-       private 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());
-               }
-       }
-
-       /**
-        * Adds a new project.
-        */
-       private 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;
-               }
-               Project newProject = new Project();
-               newProject.setName(I18n.getMessage("jsite.project.new-project.name"));
-               newProject.setInsertURI(keyPair[0]);
-               newProject.setRequestURI(keyPair[1]);
-               newProject.setEdition(-1);
-               newProject.setPath("");
-               projectListModel.add(newProject);
-               projectScrollPane.revalidate();
-               projectScrollPane.repaint();
-               projectList.setSelectedIndex(projectListModel.indexOf(newProject));
-       }
-
-       /**
-        * Deletes the currently selected project.
-        */
-       private 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));
-                               }
-                       }
-               }
-       }
-
-       /**
-        * Clones the currently selected project.
-        */
-       private void actionClone() {
-               int selectedIndex = projectList.getSelectedIndex();
-               if (selectedIndex > -1) {
-                       Project newProject = new Project((Project) projectList.getSelectedValue());
-                       newProject.setName(MessageFormat.format(I18n.getMessage("jsite.project.action.clone-project.copy"), newProject.getName()));
-                       projectListModel.add(newProject);
-                       projectList.setSelectedIndex(projectListModel.indexOf(newProject));
-               }
-       }
-
-       /**
-        * Copies the request URI of the currently selected project to the
-        * clipboard.
-        */
-       private void actionCopyURI() {
-               int selectedIndex = projectList.getSelectedIndex();
-               if (selectedIndex > -1) {
-                       Project selectedProject = (Project) projectList.getSelectedValue();
-                       Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
-                       clipboard.setContents(new StringSelection(selectedProject.getFinalRequestURI(0)), this);
-                       uriCopied = true;
-               }
-       }
-
-       /**
-        * Opens a {@link KeyDialog} and lets the user manipulate the keys of the
-        * project.
-        */
-       private void actionManageKeys() {
-               int selectedIndex = projectList.getSelectedIndex();
-               if (selectedIndex > -1) {
-                       Project selectedProject = (Project) projectList.getSelectedValue();
-                       KeyDialog keyDialog = new KeyDialog(freenetInterface, wizard);
-                       keyDialog.setPrivateKey(selectedProject.getInsertURI());
-                       keyDialog.setPublicKey(selectedProject.getRequestURI());
-                       keyDialog.setVisible(true);
-                       if (!keyDialog.wasCancelled()) {
-                               String originalPublicKey = selectedProject.getRequestURI();
-                               String originalPrivateKey = selectedProject.getInsertURI();
-                               selectedProject.setInsertURI(keyDialog.getPrivateKey());
-                               selectedProject.setRequestURI(keyDialog.getPublicKey());
-                               if (!originalPublicKey.equals(selectedProject.getRequestURI()) || !originalPrivateKey.equals(selectedProject.getInsertURI())) {
-                                       selectedProject.setEdition(-1);
-                               }
-                               updateCompleteURI();
-                       }
-               }
-       }
-
-       /**
-        * Resets the edition of the currently selected project.
-        */
-       private void actionResetEdition() {
-               if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.reset-edition"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
-                       return;
-               }
-               int selectedIndex = projectList.getSelectedIndex();
-               if (selectedIndex > -1) {
-                       Project selectedProject = (Project) projectList.getSelectedValue();
-                       selectedProject.setEdition(-1);
-                       updateCompleteURI();
-               }
-       }
-
-       /**
-        * Updates the complete URI text field.
-        */
-       private void updateCompleteURI() {
-               int selectedIndex = projectList.getSelectedIndex();
-               if (selectedIndex > -1) {
-                       Project selectedProject = (Project) projectList.getSelectedValue();
-                       projectCompleteUriTextField.setText(selectedProject.getFinalRequestURI(0));
-               }
-       }
-
-       //
-       // 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);
-               projectPathTextField.setEnabled(selectedRow > -1);
-               projectLocalPathBrowseAction.setEnabled(selectedRow > -1);
-               projectDeleteAction.setEnabled(selectedRow > -1);
-               projectCloneAction.setEnabled(selectedRow > -1);
-               projectCopyURIAction.setEnabled(selectedRow > -1);
-               projectManageKeysAction.setEnabled(selectedRow > -1);
-               projectResetEditionAction.setEnabled(selectedRow > -1);
-               if (selectedRow > -1) {
-                       projectNameTextField.setText(selectedProject.getName());
-                       projectDescriptionTextField.setText(selectedProject.getDescription());
-                       projectLocalPathTextField.setText(selectedProject.getLocalPath());
-                       projectPathTextField.setText(selectedProject.getPath());
-                       projectCompleteUriTextField.setText("freenet:" + selectedProject.getFinalRequestURI(0));
-               } else {
-                       projectNameTextField.setText("");
-                       projectDescriptionTextField.setText("");
-                       projectLocalPathTextField.setText("");
-                       projectPathTextField.setText("");
-                       projectCompleteUriTextField.setText("");
-               }
-       }
-
-       //
-       // INTERFACE ChangeListener
-       //
-
-       //
-       // 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);
-       }
-
-       //
-       // INTERFACE ClipboardOwner
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void lostOwnership(Clipboard clipboard, Transferable contents) {
-               /* ignore. */
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/i18n/I18n.java b/src/de/todesbaum/jsite/i18n/I18n.java
deleted file mode 100644 (file)
index 4c1cceb..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * jSite - I18n.java - Copyright © 2006–2012 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.MissingResourceException;
-import java.util.ResourceBundle;
-
-/**
- * Maps i18n keys to translated texts, depending on a current locale.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class I18n {
-
-       /** The default locale, English. */
-       private static Locale defaultLocale = new Locale("en");
-
-       /** The current locale. */
-       private static Locale currentLocale;
-
-       /**
-        * Returns the currently set locale.
-        *
-        * @return The current locale
-        */
-       public static Locale getLocale() {
-               if (currentLocale == null) {
-                       currentLocale = Locale.getDefault();
-               }
-               return currentLocale;
-       }
-
-       /**
-        * Sets the current locale.
-        *
-        * @param locale
-        *            The new current locale
-        */
-       public static void setLocale(Locale locale) {
-               currentLocale = locale;
-               Locale.setDefault(locale);
-       }
-
-       /**
-        * Returns the resource bundle for the current locale.
-        *
-        * @return The resource bundle for the current locale
-        */
-       public static ResourceBundle getResourceBundle() {
-               return getResourceBundle(getLocale());
-       }
-
-       /**
-        * Returns the resource bundle for the given locale.
-        *
-        * @param locale
-        *            The locale to get the resource bundle for
-        * @return The resource bundle for the given locale
-        */
-       public static ResourceBundle getResourceBundle(Locale locale) {
-               return ResourceBundle.getBundle("de.todesbaum.jsite.i18n.jSite", locale);
-       }
-
-       /**
-        * Retrieves a translated text for the given i18n key. If the resource
-        * bundle for the current locale does not have a translation for the given
-        * key, the default locale is tried. If that fails, the key is returned.
-        *
-        * @param key
-        *            The key to get the translation for
-        * @return The translated value, or the key itself if not translation can be
-        *         found
-        */
-       public static String getMessage(String key) {
-               try {
-                       return getResourceBundle().getString(key);
-               } catch (MissingResourceException mre1) {
-                       try {
-                               return getResourceBundle(defaultLocale).getString(key);
-                       } catch (MissingResourceException mre2) {
-                               return key;
-                       }
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/i18n/I18nContainer.java b/src/de/todesbaum/jsite/i18n/I18nContainer.java
deleted file mode 100644 (file)
index da2e0c3..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * jSite - I18nContainer.java - Copyright © 2007–2012 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.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Container that collects {@link Runnable}s that change the texts of GUI
- * components when the current locale has changed.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class I18nContainer implements Iterable<Runnable> {
-
-       /** The container singleton. */
-       private static final I18nContainer singleton = new I18nContainer();
-
-       /** The list of runnables that change texts. */
-       private final List<Runnable> i18nRunnables = Collections.synchronizedList(new ArrayList<Runnable>());
-
-       /**
-        * The list of runnables that change texts and run after
-        * {@link #i18nRunnables}.
-        */
-       private final List<Runnable> i18nPostRunnables = Collections.synchronizedList(new ArrayList<Runnable>());
-
-       /**
-        * Returns the singleton instance.
-        *
-        * @return The singleton instance
-        */
-       public static I18nContainer getInstance() {
-               return singleton;
-       }
-
-       /**
-        * Registers an i18n runnable that is run when the current locale has
-        * changed.
-        *
-        * @param i18nRunnable
-        *            The runnable to register
-        */
-       public void registerRunnable(Runnable i18nRunnable) {
-               i18nRunnables.add(i18nRunnable);
-       }
-
-       /**
-        * Registers a {@link Runnable} that changes texts when the current locale
-        * has changed and runs after {@link #i18nRunnables} have run.
-        *
-        * @param i18nPostRunnable
-        *            The runnable to register
-        */
-       public void registerPostRunnable(Runnable i18nPostRunnable) {
-               i18nPostRunnables.add(i18nPostRunnable);
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * Returns a combined list of {@link #i18nRunnables} and
-        * {@link #i18nPostRunnables}, in that order.
-        */
-       public Iterator<Runnable> iterator() {
-               List<Runnable> allRunnables = new ArrayList<Runnable>();
-               allRunnables.addAll(i18nRunnables);
-               allRunnables.addAll(i18nPostRunnables);
-               return allRunnables.iterator();
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/i18n/jSite.properties b/src/de/todesbaum/jsite/i18n/jSite.properties
deleted file mode 100644 (file)
index cd80c67..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# jSite - jSite.properties - Copyright © 2006–2012 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>
-
-# Attention, translators! Most of the strings here are used directly.
-# However, some of them are parsed by MessageFormat
-# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
-# and thus have to adhere to some rules (check the URL above). This is the
-# case when a line contains placeholders like {0} or {0,number}! In these
-# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
-# otherwise the placeholder will not be replaced!
-
-jsite.general.ok=OK
-jsite.general.cancel=Cancel
-
-jsite.wizard.previous=Previous
-jsite.wizard.next=Next
-jsite.wizard.quit=Quit
-
-jsite.quit.question=Do you really want to quit?
-jsite.quit.question.title=Really quit?
-jsite.quit.overwrite-configuration=<html><b>Overwrite configuration?</b><br><br>A configuration file already exists:<br><code>{0}</code><br><br>Should it be overwritten?</html>
-jsite.quit.overwrite-configuration.title=Overwrite configuration?
-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.en=English
-jsite.menu.language.de=Deutsch
-jsite.menu.language.fr=Fran\u00e7ais
-jsite.menu.language.it=Italiano
-jsite.menu.language.pl=Polski
-jsite.menu.nodes=Nodes
-jsite.menu.nodes.manage-nodes=Manage nodes
-jsite.menu.options=Options
-jsite.menu.options.preferences=Preferences
-jsite.menu.help=Help
-jsite.menu.help.check-for-updates=Check for Updates
-jsite.menu.help.about=About
-
-jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 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.preferences.heading=Preferences
-jsite.preferences.description=Use this page to manage some global settings.
-jsite.preferences.temp-directory=Directory for temporary files
-jsite.preferences.temp-directory.default=Default (chosen by system)
-jsite.preferences.temp-directory.custom=Custom
-jsite.preferences.temp-directory.choose=Choose
-jsite.preferences.temp-directory.choose.approve=Choose
-jsite.preferences.config-directory=Location of configuration file
-jsite.preferences.config-directory.jar=Next to the JAR file
-jsite.preferences.config-directory.home=Home directory
-jsite.preferences.config-directory.custom=Custom directory
-jsite.preferences.insert-options=Insert options
-jsite.preferences.insert-options.use-early-encode=Generate final URI early
-jsite.preferences.insert-options.priority=Priority
-jsite.preferences.insert-options.manifest-putter=Manifest Putter
-
-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.starting=Starting\u2026
-jsite.insert.done=Done.
-jsite.insert.done.title=Insert done
-jsite.insert.insert-aborted=The insert was aborted.
-jsite.insert.insert-aborted.title=Insert Aborted
-jsite.insert.progress=Progress
-jsite.insert.k-per-s=KB/s
-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.insert-failed.title=Insert Failed
-jsite.insert.inserted=<html><b>Project inserted</b><br><br>Your project was inserted successfully.</html>
-jsite.insert.okay-copy-uri=Copy URI to Clipboard
-jsite.insert.reinserted-edition=<html><b>Edition Reinserted</b><br><br>The edition you are just inserting<br>has already been inserted before.</html>
-jsite.insert.reinserted-edition.title=Edition Reinserted
-
-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.action.copy-uri=Copy URI to Clipboard
-jsite.project.action.copy-uri.tooltip=Copies the URI of the project to the clipboard
-jsite.project.action.manage-keys=Manage Keys
-jsite.project.action.manage-keys.tooltip=Manages the keys of this project
-jsite.project.action.reset-edition=Reset Edition
-jsite.project.action.reset-edition.tooltip=Resets the edition number of the 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.path=Freesite Path
-jsite.project.project.edition=Edition
-jsite.project.project.uri=URI
-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.generate-new-key=<html><b>Generate new key?</b><br><br>If you generate a new key, your site will be published<br>under that new key. Any trust that other users put<br>in the old key of your site will be gone!<br>Also, the edition will be reset.</html>
-jsite.project.warning.reset-edition=<html><b>Reset edition?</b><br><br>Resetting the edition can lead to insert failures<br>and lots of confusion if you have not changed<br>the path or the keys of the project!</html>
-jsite.project.warning.use-clipboard-now=<html><b>URI copied</b><br><br>Please note that it is possible that quitting jSite<br>now will empty the clipboard. Please use the<br>copied URI immediately in another window!</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.ignore-hidden-files=Ignore hidden files
-jsite.project-files.ignore-hidden-files.tooltip=When selected, hidden files are not inserted
-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 you do not want to insert this file
-jsite.project-files.force-insert=Force insert
-jsite.project-files.force-insert.tooltip=Forces the insert of this file even it is not modified
-jsite.project-files.insert-redirect=Redirect
-jsite.project-files.insert-redirect.tooltip=Check if you want to insert a redirect for this file
-jsite.project-files.custom-key=Custom key
-jsite.project-files.custom-key.tooltip=The externally created key for the file
-jsite.project-files.rename=Rename
-jsite.project-files.rename.tooltip=Renames the file in the uploaded site
-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.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 not accessible.<br>Please go back and select the correct directory.</html>
-jsite.project-files.insert-now=Insert now
-jsite.project-files.invalid-default-file=Only files in the root directory may be selected as default files.
-
-jsite.update-checker.found-version.title=Found New Version
-jsite.update-checker.found-version.message=<html>A new version was found.<br><br>Version {0} (released {1,date})</html>
-jsite.update-checker.latest-version.title=Update Check
-jsite.update-checker.latest-version.newer.message=<html>You are running version {0} but a newer<br>version ({1}) has been found!</html>
-jsite.update-checker.latest-version.older.message=<html>You are running version {0} but the<br>latest version seems to be {1}.</html>
-jsite.update-checker.latest-version.okay.message=<html>You are currently running version {0}<br>which is the latest version.</html>
-
-jsite.key-dialog.title=Manage Project Keys
-jsite.key-dialog.button.ok.tooltip=Accepts the changes
-jsite.key-dialog.button.cancel.tooltip=Discards the changes
-jsite.key-dialog.button.generate=Regenerate Keys
-jsite.key-dialog.button.generate.tooltip=Create a new key pair
-jsite.key-dialog.label.keys=<html><b>Keys</b></html>
-jsite.key-dialog.label.private-key=Private Key
-jsite.key-dialog.label.public-key=Public Key
-jsite.key-dialog.label.actions=<html><b>Actions</b></html>
-
-jsite.warning.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.warning.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.warning.site-larger-than-2-mib=<html><b>Site is larger than 2 MiB!</b><br><br>Your site contains more than 2 megabytes of data.<br>Due to bugs in Freenet it will probably not load correctly.<br>Try to reduce the size of your site, or continue at your own peril.</html>
-
-jsite.error.no-node-selected=<html><b>No node selected</b><br><br>Please select a node from the menu!</html>
-jsite.error.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.error.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.error.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.error.index-missing=<html>Your default file is missing</b><br><br>A default file was previously specified but it<br>does not exist anymore! Please select<br>a new default file in the list of files.</html>
-jsite.error.index-not-inserted=<html><b>Default file not inserted</b><br><br>You have chosen not to insert the default file!<br>You need to either choose to insert it or select<br>a different default file!</html>
-jsite.error.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.error.no-files-to-insert=<html><b>No files to insert</b><br><br>You do not have any files selected for insertion!<br>Please select at least one file to insert.</html>
-jsite.error.duplicate-file=<html><b>Duplicate file</b><br><br>The file <code>{0}</code> is inserted twice!<br>Please check your filenames and redirects.</html>
diff --git a/src/de/todesbaum/jsite/i18n/jSite_de.properties b/src/de/todesbaum/jsite/i18n/jSite_de.properties
deleted file mode 100644 (file)
index 8641d14..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-#
-# jSite - jSite_de.properties - Copyright © 2006–2012 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>
-
-# Attention, translators! Most of the strings here are used directly.
-# However, some of them are parsed by MessageFormat
-# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
-# and thus have to adhere to some rules (check the URL above). This is the
-# case when a line contains placeholders like {0} or {0,number}! In these
-# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
-# otherwise the placeholder will not be replaced!
-
-jsite.general.ok=OK
-jsite.general.cancel=Abbrechen
-
-jsite.wizard.previous=Zur\u00fcck
-jsite.wizard.next=Vorw\u00e4rts
-jsite.wizard.quit=Beenden
-
-jsite.quit.question=M\u00f6chten Sie jSite wirklich beenden?
-jsite.quit.question.title=Wirklich beenden?
-jsite.quit.overwrite-configuration=<html><b>Konfiguration \u00fcberschreiben?</b><br><br>Es existiert bereits eine Konfigurationsdatei unter:<br><code>{0}</code><br><br>Soll sie \u00fcberschrieben werden?</html>
-jsite.quit.overwrite-configuration.title=Konfiguration \u00fcberschreiben?
-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.en=English
-jsite.menu.language.de=Deutsch
-jsite.menu.language.fr=Fran\u00e7ais
-jsite.menu.language.it=Italiano
-jsite.menu.language.pl=Polski
-jsite.menu.nodes=Nodes
-jsite.menu.nodes.manage-nodes=Nodes verwalten
-jsite.menu.options=Optionen
-jsite.menu.options.preferences=Einstellungen
-jsite.menu.help=Hilfe
-jsite.menu.help.check-for-updates=Auf Updates pr\u00fcfen
-jsite.menu.help.about=\u00dcber
-
-jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 David Roden<br>Ver\u00f6ffentlicht 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\u00fcgen
-jsite.node-manager.new-node=Neuer Node
-jsite.node-manager.delete-node=Node l\u00f6schen
-jsite.node-manager.delete-node.warning=<html><b>Nodel\u00f6schung best\u00e4tigen</b><br><br>Wollen Sie diesen Node wirklich l\u00f6schen?</html>
-jsite.node-manager.name=Name
-jsite.node-manager.hostname=Hostname
-jsite.node-manager.port=Port
-
-jsite.preferences.heading=Einstellungen
-jsite.preferences.description=Auf dieser Seite k\u00f6nnen einige globale Einstellungen vorgenommen werden.
-jsite.preferences.temp-directory=Verzeichnis f\u00fcr tempor\u00e4re Dateien
-jsite.preferences.temp-directory.default=Standard (vom System bestimmt)
-jsite.preferences.temp-directory.custom=Eigenes
-jsite.preferences.temp-directory.choose=Ausw\u00e4hlen
-jsite.preferences.temp-directory.choose.approve=Ausw\u00e4hlen
-jsite.preferences.config-directory=Lage der Konfigurationsdatei
-jsite.preferences.config-directory.jar=Neben der JAR-Datei
-jsite.preferences.config-directory.home=Benutzerverzeichnis
-jsite.preferences.config-directory.custom=Angegebenes Verzeichnis
-jsite.preferences.insert-options=Einf\u00fcgeoptionen
-jsite.preferences.insert-options.use-early-encode=Endg\u00fcltige URI fr\u00fcher berechnen
-jsite.preferences.insert-options.priority=Priorit\u00e4t
-jsite.preferences.insert-options.manifest-putter=Manifesterstellung
-
-jsite.insert.heading=Projekt einf\u00fcgen
-jsite.insert.description=Bitte warten Sie, w\u00e4hrend das Projekt eingef\u00fcgt wird.
-jsite.insert.project-information=Projektinformationen
-jsite.insert.request-uri=Freesite
-jsite.insert.start-time=Beginn
-jsite.insert.starting=Beginne\u2026
-jsite.insert.done=Fertig.
-jsite.insert.done.title=Einf\u00fcgen abgeschlossen
-jsite.insert.insert-aborted=Das Einf\u00fcgen wurde abgebrochen.
-jsite.insert.insert-aborted.title=Einf\u00fcgen abgebrochen
-jsite.insert.progress=Fortschritt
-jsite.insert.k-per-s=KB/s
-jsite.insert.insert-failed=<html><b>Einf\u00fcgen fehlgeschlagen</b><br><br>Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingef\u00fcgt werden konnten.</html>
-jsite.insert.insert-failed-with-cause=<html><b>Einf\u00fcgen fehlgeschlagen</b><br><br>Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingef\u00fcgt werden konnten.<br>Folgender Fehler trat auf:<br><br><code>{0}</code></html>
-jsite.insert.insert-failed.title=Einf\u00fcgen fehlgeschlagen
-jsite.insert.inserted=<html><b>Projekt eingef\u00fcgt</b><br><br>Ihr Projekt wurde erfolgreich eingef\u00fcgt.</html>
-jsite.insert.okay-copy-uri=URI kopieren
-jsite.insert.reinserted-edition=<html><b>Edition bereits eingef\u00fcgt</b><br><br>Die Edition, die gerade eingef\u00fcgt wird,<br>ist schon einmal eingef\u00fcgt worden.</html>
-jsite.insert.reinserted-edition.title=Edition bereits eingef\u00fcgt
-
-jsite.file-scanner.can-not-read-directory=Kann Verzeichnis nicht lesen
-
-jsite.project.heading=Projekt ausw\u00e4hlen
-jsite.project.description=W\u00e4hlen Sie das Projekt aus, welches sie einf\u00fcgen m\u00f6chten, oder erstellen Sie ein neues Projekt.
-jsite.project.action.browse=Durchsuchen
-jsite.project.action.browse.choose=Ausw\u00e4hlen
-jsite.project.action.browse.tooltip=Lokalen Pfad f\u00fcr Projekt ausw\u00e4hlen
-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\u00f6schen
-jsite.project.action.delete-project.tooltip=Ein Projekt l\u00f6schen
-jsite.project.action.delete-project.confirm=<html><b>L\u00f6schung best\u00e4tigen</b><br><br>Das Projekt \u201e{0}\u201c wird gel\u00f6scht!<br>M\u00f6chten 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\u00e4hlte Projekt duplizieren
-jsite.project.action.copy-uri=URI kopieren
-jsite.project.action.copy-uri.tooltip=Kopiert die URI des ausgew\u00e4hlten Projektes in die Zwischenablage
-jsite.project.action.manage-keys=Schl\u00fcsselverwaltung
-jsite.project.action.manage-keys.tooltip=Verwaltet die Schl\u00fcssel des Projekts
-jsite.project.action.reset-edition=Edition zur\u00fccksetzen
-jsite.project.action.reset-edition.tooltip=Setzt die Editionsnummer des Projekts zur\u00fcck
-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.path=Seitenpfad
-jsite.project.project.edition=Edition
-jsite.project.project.uri=Anfrage-URI
-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\u00e4uft und dass Sie<br> den korrekten Hostnamen und die korrekte Portnummer auf der<br>\u201eNode Einstellungen\u201c Seite eingegeben haben.</html>
-jsite.project.warning.generate-new-key=<html><b>Neues Schl\u00fcsselpaar generieren?</b><br><br>Wenn Sie das Schl\u00fcsselpaar f\u00fcr das Projekt \u00e4ndern,<br>wird sich die URI f\u00fcr Ihr Projekt ebenfalls<br>\u00e4ndern, und jegliches Vertrauen, dass andere<br>Benutzer in das alte Schl\u00fcsselpaar hatten, wird<br>verloren gehen! Au\u00dferdem wird die Edition zur\u00fcckgesetzt.</html>
-jsite.project.warning.reset-edition=<html><b>Edition zur\u00fccksetzen?</b><br><br>Das Zur\u00fccksetzen der Editionsnummer kann zum<br>Fehlschlagen des Einf\u00fcgens f\u00fchren, wenn sich nicht<br>auch die URI oder der Pfad des Projekts ge\u00e4ndert haben!</html>
-jsite.project.warning.use-clipboard-now=<html><b>Anfrage-URI kopiert</b><br><br>Bitte beachten Sie, dass die Zwischenablage nach dem<br>Beenden von jSite eventuell nicht mehr die kopierte<br>URI enth\u00e4lt. Bitte f\u00fcgen Sie sie daher schleunigst in<br>ein anderes Programm ein!</html>
-
-jsite.project-files.heading=Projektdateien
-jsite.project-files.description=<html>Auf dieser Seite k\u00f6nnen Parameter f\u00fcr die einzelnen Dateien dieses Projekts angegeben werden, z.B.<br>extern erstellte Schl\u00fcssel 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.ignore-hidden-files=Versteckte Dateien ignorieren
-jsite.project-files.ignore-hidden-files.tooltip=Verhindert, dass versteckte Dateien hochgeladen werden
-jsite.project-files.file-options=Dateioptionen
-jsite.project-files.default=Index-Datei
-jsite.project-files.default.tooltip=Lege Index-Datei f\u00fcr Projekt fest
-jsite.project-files.insert=Einf\u00fcgen
-jsite.project-files.insert.tooltip=jSite f\u00fcgt diese Datei ein
-jsite.project-files.force-insert=Einf\u00fcgen erzwingen
-jsite.project-files.force-insert.tooltip=F\u00fcgt diese Datei ein, auch wenn sie nicht modifiziert wurde
-jsite.project-files.insert-redirect=Umleitung
-jsite.project-files.insert-redirect.tooltip=F\u00fcgt eine Umleitung ein
-jsite.project-files.custom-key=Extern erstellter Schl\u00fcssel
-jsite.project-files.custom-key.tooltip=Der extern erstellte Schl\u00fcssel f\u00fcr diese Datei
-jsite.project-files.rename=Umbenennen
-jsite.project-files.rename.tooltip=Benennt die Datei in der eingef\u00fcgten Seite um
-jsite.project-files.mime-type=MIME-Typ
-jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier ausw\u00e4hlen, wenn die automatische Erkennenung falsch ist
-jsite.project-files.container=Container
-jsite.project-files.container.tooltip=W\u00e4hlt einen Container f\u00fcr diese Datei aus
-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\u00fcck und beheben Sie den Fehler!</html>
-jsite.project-files.insert-now=Jetzt einf\u00fcgen
-jsite.project-files.invalid-default-file=Nur Dateien im obersten Verzeichnis d\u00fcrfen als Index-Dateien ausgew\u00e4hlt werden.
-
-jsite.update-checker.found-version.title=Neue Version gefunden
-jsite.update-checker.found-version.message=<html>Eine neue Version wurde gefunden.<br><br>Version {0} (ver\u00f6ffentlicht {1,date})</html>
-jsite.update-checker.latest-version.title=Update\u00fcberpr\u00fcfung
-jsite.update-checker.latest-version.newer.message=<html>Es l\u00e4uft momentan Version {0}, aber eine<br>neue Version ({1}) wurde bereits gefunden!</html>
-jsite.update-checker.latest-version.older.message=<html>Es l\u00e4uft momentan Version {0}, aber die<br>aktuelle Version ist erst {1}.</html>
-jsite.update-checker.latest-version.okay.message=<html>Es l\u00e4uft momentan Version {0},<br>und diese Version ist aktuell.</html>
-
-jsite.key-dialog.title=Projektschl\u00fcsselverwaltung
-jsite.key-dialog.button.ok.tooltip=\u00c4nderungen akzeptieren
-jsite.key-dialog.button.cancel.tooltip=\u00c4nderungen verwerfen
-jsite.key-dialog.button.generate=Schl\u00fcssel neu generieren
-jsite.key-dialog.button.generate.tooltip=Generiert ein neues Schl\u00fcsselpaar
-jsite.key-dialog.label.keys=<html><b>Schl\u00fcssel</b></html>
-jsite.key-dialog.label.private-key=Privater Schl\u00fcssel
-jsite.key-dialog.label.public-key=\u00d6ffentlicher Schl\u00fcssel
-jsite.key-dialog.label.actions=<html><b>Aktionen</b></html>
-
-jsite.warning.empty-index=<html><b>Keine Index-Datei gew\u00e4hlt</b><br><br>Sie haben keine Index-Datei f\u00fcr das Projekt angegeben.<br>Obwohl es m\u00f6glich ist, das zu machen, sollten Sie doch<br>eine Index-Datei angeben, um das Browsen zu erleichtern.</html>
-jsite.warning.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\u00fchren.</html>
-jsite.warning.site-larger-than-2-mib=<html><b>Ihr Projekt ist gr\u00f6\u00dfer als 2 Megabyte!</b><br><br>Ihr Projekt enth\u00e4lt mehr als 2 Megabyte an Daten. Aufgrund<br>eines Fehlers in Freenet wird die Seite wahrscheinlich nicht<br>korrekt angezeigt werden. Bitte reduzieren Sie die Gr\u00f6\u00dfe<br>Ihres Projektes, oder fahren Sie auf eigene Gefahr fort.</html>
-
-jsite.error.no-node-selected=<html><b>Kein Node ausgew\u00e4hlt</b><br><br>Bitte w\u00e4hlen Sie einen Node aus dem Men\u00fc!</html>
-jsite.error.no-node-running=<html><b>Der Node l\u00e4uft nicht</b><br><br>Sie k\u00f6nnen das Projekt nicht einf\u00fcgen, wenn<br>Ihr Node nicht l\u00e4uft. Bitte starten Sie Ihren Node<br>und probieren Sie es erneut.</html>
-jsite.error.no-local-path=<html><b>Kein lokaler Pfad</b><br><br>Sie haben keinen lokalen Pfad f\u00fcr die einzuf\u00fcgenden Dateien angegeben.<br>Es ist nicht m\u00f6glich, ohne lokalen Pfad weiter zu machen.</html>
-jsite.error.no-path=<html><b>Kein Freesite-Pfad</b><br><br>Sie haben keinen Pfad f\u00fcr die Freesite angegeben.<br>Es ist nicht m\u00f6glich, ohne einen Freesite-Pfad weiter zu machen.</html>
-jsite.error.index-missing=<html><b>Index-Datei fehlt!</b><br><br>Sie haben eine Index-Datei f\u00fcr das Project gew\u00e4hlt,<br>aber diese Index-Datei existiert nicht mehr!<br>Bitte w\u00e4hlen Sie eine neue Index-Datei.</html>
-jsite.error.index-not-inserted=<html><b>Index-Datei nicht eingef\u00fcgt</b><br><br>Die index-Datei ist nicht zum Einf\u00fcgen ausgew\u00e4hlt!<br>Sie m\u00fcssen entweder w\u00e4hlen, die Index-Datei einzuf\u00fcgen,<br>oder Sie m\u00fcssen eine andere Index-Datei ausw\u00e4hlen!</html>
-jsite.error.no-custom-key=<html><b>Kein externer Schl\u00fcssel</b><br><br>Sie haben angegeben, dass die Datei <code>{0}</code><br>nicht eingef\u00fcgt werden soll. Allerdings haben Sie<br>keinen extern erstellten Schl\u00fcssel angegeben.</html>
-jsite.error.no-files-to-insert=<html><b>Keine Dateien einzuf\u00fcgen</b><br><br>Es sind keine Dateien zum Einf\u00fcgen ausgew\u00e4hlt! Bitte<br>w\u00e4hlen Sie mindestens eine Datei zum Einf\u00fcgen aus!</html>
-jsite.error.duplicate-file=<html><b>Doppelte Datei</b><br><br>Die Datei <code>{0}</code> wird zweimal eingef\u00fcgt!<br>Bitte \u00fcberpr\u00fcfen Sie Ihre Umleitungen und Dateinamen!</html>
diff --git a/src/de/todesbaum/jsite/i18n/jSite_fr.properties b/src/de/todesbaum/jsite/i18n/jSite_fr.properties
deleted file mode 100644 (file)
index 39f9acd..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-#
-# jSite - jSite_fr.properties - Copyright © 2006–2012 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.
-#
-
-# French language file by Florent Daigni\u00e8re <nextgens@freenetproject.org>, Julien Cornuwel <batosai@batosai.net>, and Clement Vollet <cvollet@gmail.com>.
-
-# Attention, translators! Most of the strings here are used directly.
-# However, some of them are parsed by MessageFormat
-# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
-# and thus have to adhere to some rules (check the URL above). This is the
-# case when a line contains placeholders like {0} or {0,number}! In these
-# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
-# otherwise the placeholder will not be replaced!
-
-jsite.general.ok=OK
-jsite.general.cancel=Annuler
-
-jsite.wizard.previous=Pr\u00e9c\u00e9dent
-jsite.wizard.next=Suivant
-jsite.wizard.quit=Quitter
-
-jsite.quit.question=Voulez-vous r\u00e9ellement quitter?
-jsite.quit.question.title=Souhaitez vous quitter?
-jsite.quit.overwrite-configuration=<html><b>Ecraser la configuration?</b><br><br>Un fichier de configuration éxiste déjà:<br><code>{0}</code><br><br>Doit-il être écrasé ?</html>
-jsite.quit.overwrite-configuration.title=Ecraser la configuration?
-jsite.quit.config-not-saved=<html><b>Configuration non sauvegard\u00e9e</b><br><br>La configuration n'a pas pu \u00eatre sauv\u00e9e.<br>Voulez vous quitter tout de m\u00eame?</html>
-
-jsite.menu.languages=Langue
-jsite.menu.language.en=English
-jsite.menu.language.de=Deutsch
-jsite.menu.language.fr=Fran\u00e7ais
-jsite.menu.language.it=Italiano
-jsite.menu.language.pl=Polski
-jsite.menu.nodes=Noeud
-jsite.menu.nodes.manage-nodes=G\u00e9rer les noeuds
-jsite.menu.options=Options
-jsite.menu.options.preferences=Pr\u00e9f\u00e9rences
-jsite.menu.help=Aide
-jsite.menu.help.check-for-updates=Mises \u00e0 jour
-jsite.menu.help.about=A propos de jSite
-
-jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 David Roden<br>Publié sous GNU General Public License</html>
-
-jsite.node-manager.heading=Gestionnaire de noeud
-jsite.node-manager.description=G\u00e9rez vos noeuds.
-jsite.node-manager.node-information=Information sur le noeud
-jsite.node-manager.add-node=Ajouter un noeud
-jsite.node-manager.new-node=Nouveau noeud
-jsite.node-manager.delete-node=Supprimer un noeud
-jsite.node-manager.delete-node.warning=<html><b>Confirmer la suppression</b><br><br>Supprimer r\u00e9ellement ce noeud?</html>
-jsite.node-manager.name=Nom
-jsite.node-manager.hostname=Nom de machine
-jsite.node-manager.port=Port
-
-jsite.preferences.heading=Pr\u00e9f\u00e9rences
-jsite.preferences.description=Utilisez cette page pour g\u00e9rer quelques param\u00e8tres globaux.
-jsite.preferences.temp-directory=R\u00e9pertoire des fichiers temporaires
-jsite.preferences.temp-directory.default=D\u00e9faut (choix syst\u00e8mes)
-jsite.preferences.temp-directory.custom=Personnalis\u00e9
-jsite.preferences.temp-directory.choose=Choisir
-jsite.preferences.temp-directory.choose.approve=Choisir
-jsite.preferences.config-directory=Chemin du fichier de configuration
-jsite.preferences.config-directory.jar=Suivant, le fichier JAR
-jsite.preferences.config-directory.home=Acceuil
-jsite.preferences.config-directory.custom=Répertoire personnel
-jsite.preferences.insert-options=Options d'insertion
-jsite.preferences.insert-options.use-early-encode=Générer d'abord l'URI
-jsite.preferences.insert-options.priority=Priorité
-jsite.preferences.insert-options.manifest-putter=Ajout de Manifest
-
-jsite.insert.heading=Projet d'insertion
-jsite.insert.description=Veuillez attendre durant l'insertion du projet.
-jsite.insert.project-information=Information \u00e0 propos du projet.
-jsite.insert.request-uri=Freesite
-jsite.insert.start-time=Commenc\u00e9 \u00e0
-jsite.insert.starting=D\u00e9marrage\u2026
-jsite.insert.done=Termin\u00e9.
-jsite.insert.done.title=Insertion effectu\u00e9e
-jsite.insert.insert-aborted=L'insertion a été annulée.
-jsite.insert.insert-aborted.title=Insertion Annulée
-jsite.insert.progress=Avancement
-jsite.insert.k-per-s=Ko/s
-jsite.insert.insert-failed=<html><b>Insertion \u00e9chou\u00e9e</b><br><br>L'insertion du projet \u00e0 \u00e9chou\u00e9e.<br>Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s.</html>
-jsite.insert.insert-failed-with-cause=<html><b>Insertion \u00e9chou\u00e9e</b><br><br>L'insertion du projet \u00e0 \u00e9chou\u00e9e.<br>Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s.<br>L'erreur suivante s'est produite:<br><br><code>{0}</code></html>
-jsite.insert.insert-failed.title=Insertion Echouée
-jsite.insert.inserted=<html><b>Projet ins\u00e9r\u00e9!</b><br><br>Votre projet \u00e0 \u00e9t\u00e9 correctement ins\u00e9r\u00e9.</html>
-jsite.insert.okay-copy-uri=Copier l'URI vers le presse-papiers
-jsite.insert.reinserted-edition=<html><b>Edition r\u00e9ins\u00e9r\u00e9e</b><br><br>L'\u00e9dition que vous \u00eates en train d'ins\u00e9rer<br>a d\u00e9j\u00e0 \u00e9t\u00e9 ins\u00e9r\u00e9e avant.</html>
-jsite.insert.reinserted-edition.title=Edition r\u00e9ins\u00e9r\u00e9e
-
-jsite.file-scanner.can-not-read-directory=Impossible de lire le r\u00e9pertoire
-
-jsite.project.heading=S\u00e9lectionnez un projet
-jsite.project.description=S\u00e9lectionnez un projet de la liste ou cr\u00e9ez en un nouveau.
-jsite.project.action.browse=Naviguer
-jsite.project.action.browse.choose=Choisir
-jsite.project.action.browse.tooltip=Choisir un r\u00e9pertoire
-jsite.project.action.add-project=Ajouter un projet
-jsite.project.action.add-project.tooltip=Ajouter un projet
-jsite.project.new-project.name=Nouveau projet
-jsite.project.action.delete-project=D\u00e9truire le projet
-jsite.project.action.delete-project.tooltip=D\u00e9truire le projet
-jsite.project.action.delete-project.confirm=<html><b>Confirmez la suppression</b><br><br>Le projet \u201c{0}\u201d va \u00eatre d\u00e9truit!<br>Voulez vous poursuivre?</html>
-jsite.project.action.clone-project=Cloner le projet
-jsite.project.action.clone-project.copy=Copie de {0}
-jsite.project.action.clone-project.tooltip=Cloner le projet s\u00e9lectionn\u00e9
-jsite.project.action.copy-uri=Copier l'URI dans le presse-papier
-jsite.project.action.copy-uri.tooltip=Copie l'URI du projet dans le presse-papier
-jsite.project.action.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9
-jsite.project.action.generate-new-key.tooltip=Cr\u00e9e une nouvelle cl\u00e9 pour ce projet
-jsite.project.action.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition
-jsite.project.action.reset-edition.tooltip=Remettre \u00e0 z\u00e9ro l'\u00e9dition du projet
-jsite.project.project.information=Informations concernant le projet
-jsite.project.project.name=Nom
-jsite.project.project.description=Description
-jsite.project.project.local-path=Chemin local
-jsite.project.project.address=Adresse
-jsite.project.project.path=Chemin du freesite
-jsite.project.project.edition=Edition
-jsite.project.project.uri=URI
-jsite.project.keygen.io-error=<html><b>Erreur de communication avec le noeud</b><br><br>La communication avec le noeud \u00e0 \u00e9chou\u00e9e<br>Erreur:<br><br><code>{0}</code><br><br>Assurez vous que les informations saisies dans la page de configuration sont correctes.</html>
-jsite.project.warning.generate-new-key=<html><b>G\u00e9n\u00e9rer une nouvelle cl\u00e9 ?</b><br><br>Si vous g\u00e9n\u00e9rez une nouvelle cl\u00e9, votre site sera publi\u00e9<br>avec cette nouvelle cl\u00e9. La confiance que les autres<br>utilisateurs pla\u00e7aient dans l'ancienne cl\u00e9 sera perdue !</html>
-jsite.project.warning.reset-edition=<html><b>Remettre \u00e0 z\u00e9ro l'\u00e9dition ?</b><br><br>Remettre \u00e0 z\u00e9ro l'\u00e9dition peut faire \u00e9chouer l'insertion<br> ou poser des probl\u00e8mes si vous n'avez pas chang\u00e9<br>le chemin ou les cl\u00e9s du projet !</html>
-jsite.project.warning.use-clipboard-now=<html><b>URI copi\u00e9e</b><br><br>Veuillez noter qu'il est possible qu'en quittant jSite<br>maintenant le presse-papiers soit vid\u00e9. Merci d'utiliser<br>l'URI copi\u00e9e imm\u00e9diatement dans une autre fen\u00eatre !</html>
-
-jsite.project-files.heading=Fichiers du projet
-jsite.project-files.description=<html>Dans cette page vous pouvez sp\u00e9cifier les informations concernant la configuration des noeuds telles que:<br>Le type de contenu mime si l'auto d\u00e9tection \u00e0 \u00e9chou\u00e9e.</html>
-jsite.project-files.action.rescan=Re-scan
-jsite.project-files.action.rescan.tooltip=V\u00e9rifier la pr\u00e9sence de nouveau fichiers
-jsite.project-files.ignore-hidden-files=Ignorer les fichiers cach\u00e9s
-jsite.project-files.ignore-hidden-files.tooltip=Si s\u00e9lectionn\u00e9, les fichiers cach\u00e9s ne sont pas ins\u00e9r\u00e9s
-jsite.project-files.file-options=Option des fichiers
-jsite.project-files.default=Fichier par d\u00e9faut
-jsite.project-files.default.tooltip=Est-ce l'index?
-jsite.project-files.insert=Ins\u00e9rer
-jsite.project-files.insert.tooltip=D\u00e9cochez si vous ne voulez pas ins\u00e9rer ce fichier
-jsite.project-files.force-insert=Forcer l'insertion
-jsite.project-files.force-insert.tooltip=Forcer l'insertion de ce fichier tant qu'il n'est pas modifié
-jsite.project-files.insert-redirect=Redirection
-jsite.project-files.insert-redirect.tooltip=Cochez si vous voulez ins\u00e9rer une redirection pour ce fichier
-jsite.project-files.custom-key=Clef existante
-jsite.project-files.custom-key.tooltip=Utiliser une clef existante pour ce fichier
-jsite.project-files.rename=Renommer
-jsite.project-files.rename.tooltip=Renomme le fichier dans le site ins\u00e9r\u00e9
-jsite.project-files.mime-type=MIME type
-jsite.project-files.mime-type.tooltip=S\u00e9lectionez le type MIME du fichier si la d\u00e9tection \u00e0 \u00e9chou\u00e9e
-jsite.project-files.container=Container
-jsite.project-files.container.tooltip=S\u00e9lectionnez un container pour le fichier
-jsite.project-files.scan-error=<html><b>Erreur lors du parcours des fichiers</b><br><br>Soit le r\u00e9pertoire du projet n'existe pas,<br>ou des fichiers/r\u00e9pertoires sont inaccessibles.<br>Veuillez revenir en arri\u00e8re et s\u00e9lectionner un autre r\u00e9pertoire.</html>
-jsite.project-files.insert-now=Ins\u00e9rer
-jsite.project-files.invalid-default-file=Seulement les fichiers de la racine peuvent être selectionnés comme fichiers par defaut
-
-jsite.update-checker.found-version.title=Nouvelle version disponible
-jsite.update-checker.found-version.message=<html>Une nouvelle version est disponible.<br><br>Version {0} (publi\u00e9e le {1,date})</html>
-jsite.update-checker.latest-version.title=Recherche de mises \u00e0 jour
-jsite.update-checker.latest-version.newer.message=<html>Vous utilisez la version {0} mais une version<br>plus r\u00e9cente ({1}) est disponible!</html>
-jsite.update-checker.latest-version.older.message=<html>Vous utilisez la version {0} mais la<br>derni\u00e8re version semble \u00eatre la {1}.</html>
-jsite.update-checker.latest-version.okay.message=<html>Vous utilisez la version {0}<br>qui semble \u00eatre la derni\u00e8re.</html>
-
-jsite.key-dialog.title=G\u00e9rer les cl\u00e9s des projets
-jsite.key-dialog.button.ok.tooltip=Accepter les changements
-jsite.key-dialog.button.cancel.tooltip=Annuler les changements
-jsite.key-dialog.button.generate=Reg\u00e9n\u00e9rer les cl\u00e9s
-jsite.key-dialog.button.generate.tooltip=Cr\u00e9er une nouvelle paire de cl\u00e9s
-jsite.key-dialog.label.keys=<html><b>Cl\u00e9s</b></html>
-jsite.key-dialog.label.private-key=Cl\u00e9 priv\u00e9e
-jsite.key-dialog.label.public-key=Cl\u00e9 publique
-jsite.key-dialog.label.actions=<html><b>Actions</b></html>
-
-jsite.warning.empty-index=<html><b>Pas de fichier par d\u00e9faut</b><br><br>Avez vous sp\u00e9cifi\u00e9 un fichier par d\u00e9faut pour le projet?<br>M\u00eame s'il est possible de ne pas en sp\u00e9cifier, c'est g\u00e9n\u00e9ralement une mauvaise id\u00e9e.</html>
-jsite.warning.index-not-html=<html><b>Le fichier principal n'est pas un fichier HTML!</b><br><br>Votre fichier par d\u00e9faut n'est pas du type MIME "text/html"!<br>Chargez ce type de fichiers dans un navigateur peut \u00eatre dangereux.</html>
-
-jsite.error.no-node-selected=<html><b>Pas de noeud s\u00e9lectionn\u00e9</b><br><br>S\u00e9lectionnez un noeud dans le menu!</html>
-jsite.error.no-node-running=<html><b>Ce noeud n'est pas actif!</b><br><br>Vous ne pouvez pas utiliser jSite sans noeud actif.<br>Veuillez d\u00e9marrer votre noeud et r\u00e9essayer.</html>
-jsite.error.no-local-path=<html><b>Pas de chemin local sp\u00e9cifi\u00e9</b><br><br>Vous avez omis de sp\u00e9cifier le chemin local \u00e0 ins\u00e9rer.</html>
-jsite.error.no-path=<html><b>Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite</b><br><br>Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite.<br>Ce champ est n\u00e9cessaire.</html>
-jsite.error.index-missing=<html>Votre fichier par d\u00e9faut est manquant</b><br><br>Un fichier par d\u00e9faut est sp\u00e9cifi\u00e9 mais il<br>n'existe plus ! S\u00e9lectionnez un nouveau<br>fichier par d\u00e9faut dans la liste.</html>
-jsite.error.index-not-inserted=<html><b>Fichier par d\u00e9faut non ins\u00e9r\u00e9</b><br><br>Vous avez choisi de ne pas ins\u00e9rer le fichier par d\u00e9faut !<br>Vous devez soit l'ins\u00e9rer soit choisir<br>un fichier par d\u00e9faut diff\u00e9rent !</html>
-jsite.error.no-custom-key=<html><b>Pas de clef existante sp\u00e9cifi\u00e9e pour ce fichier</b><br><br>Vous avez sp\u00e9cifier de ne pas ins\u00e9rer <code>{0}</code><br> mais n'avez pas sp\u00e9cifier de clef ou rediriger!</html>
-jsite.error.no-files-to-insert=<html><b>Aucun fichier \u00e0 ins\u00e9rer</b><br><br>Vous n'avez s\u00e9lectionn\u00e9 aucun fichier pour l'insertion !<br>Veuillez s\u00e9lectionner au moins un fichier \u00e0 ins\u00e9rer.</html>
-jsite.error.duplicate-file=<html><b>Fichier dupliqu\u00e9</b><br><br>Le fichier <code>{0}</code> est ins\u00e9r\u00e9 deux fois !<br>Veuillez v\u00e9rifier les noms de fichier et les redirections.</html>
diff --git a/src/de/todesbaum/jsite/main/CLI.java b/src/de/todesbaum/jsite/main/CLI.java
deleted file mode 100644 (file)
index 65c9253..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * jSite - CLI.java - Copyright © 2006–2012 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.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;
-import de.todesbaum.util.io.StreamCopier.ProgressListener;
-
-/**
- * Command-line interface for jSite.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class CLI implements InsertListener {
-
-       /** Object used for synchronization. */
-       private Object lockObject = new Object();
-
-       /** Writer for the console. */
-       private PrintWriter outputWriter = new PrintWriter(System.out, true);
-
-       /** The freenet interface. */
-       private Freenet7Interface freenetInterface;
-
-       /** The project inserter. */
-       private ProjectInserter projectInserter = new ProjectInserter();
-
-       /** The list of nodes. */
-       private Node[] nodes;
-
-       /** The projects. */
-       private Project[] projects;
-
-       /** Whether the insert has finished. */
-       private boolean finished = false;
-
-       /** Whether the insert finished successfully. */
-       private boolean success;
-
-       /**
-        * Creates a new command-line interface.
-        *
-        * @param args
-        *            The command-line arguments
-        */
-       private CLI(String[] args) {
-
-               if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) {
-                       outputWriter.println("\nParameters:\n");
-                       outputWriter.println("  --config-file=<configuration file>");
-                       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;
-               }
-
-               String configFile = System.getProperty("user.home") + "/.jSite/config7";
-               for (String argument : args) {
-                       String value = argument.substring(argument.indexOf('=') + 1).trim();
-                       if (argument.startsWith("--config-file=")) {
-                               configFile = value;
-                       }
-               }
-
-               ConfigurationLocator configurationLocator = new ConfigurationLocator();
-               if (configFile != null) {
-                       configurationLocator.setCustomLocation(configFile);
-               }
-               Configuration configuration = new Configuration(configurationLocator, configurationLocator.findPreferredLocation());
-
-               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 (String argument : args) {
-                       if (argument.startsWith("--config-file=")) {
-                               /* we already parsed this one. */
-                               continue;
-                       }
-                       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;
-                               }
-                               currentProject.setEdition(Integer.parseInt(value));
-                       } else {
-                               outputWriter.println("Unknown parameter: " + argument);
-                               return;
-                       }
-               }
-
-               int errorCode = 1;
-               if (currentProject != null) {
-                       if (insertProject(currentProject)) {
-                               outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted.");
-                               errorCode = 0;
-                       } else {
-                               outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted.");
-                       }
-               }
-
-               configuration.setProjects(projects);
-               configuration.save();
-
-               System.exit(errorCode);
-       }
-
-       /**
-        * Returns the project with the given name.
-        *
-        * @param name
-        *            The name of the project
-        * @return The project, or <code>null</code> if no project could be found
-        */
-       private Project getProject(String name) {
-               for (Project project : projects) {
-                       if (project.getName().equals(name)) {
-                               return project;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Returns the node with the given name.
-        *
-        * @param name
-        *            The name of the node
-        * @return The node, or <code>null</code> if no node could be found
-        */
-       private Node getNode(String name) {
-               for (Node node : nodes) {
-                       if (node.getName().equals(name)) {
-                               return node;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Inserts the given project.
-        *
-        * @param currentProject
-        *            The project to insert
-        * @return <code>true</code> if the insert finished successfully,
-        *         <code>false</code> otherwise
-        */
-       private boolean insertProject(Project currentProject) {
-               if (!freenetInterface.hasNode()) {
-                       outputWriter.println("Node is not running!");
-                       return false;
-               }
-               projectInserter.setProject(currentProject);
-               projectInserter.start(new ProgressListener() {
-
-                       public void onProgress(long copied, long length) {
-                               System.out.print("Uploaded: " + copied + " / " + length + " bytes...\r");
-                       }
-               });
-               synchronized (lockObject) {
-                       while (!finished) {
-                               try {
-                                       lockObject.wait();
-                               } catch (InterruptedException e) {
-                                       /* ignore, we're in a loop. */
-                               }
-                       }
-               }
-               return success;
-       }
-
-       //
-       // INTERFACE InsertListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectInsertStarted(Project project) {
-               outputWriter.println("Starting Insert of project \"" + project.getName() + "\".");
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectUploadFinished(Project project) {
-               outputWriter.println("Project \"" + project.getName() + "\" has been uploaded, starting insert...");
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void projectURIGenerated(Project project, String uri) {
-               outputWriter.println("URI: " + uri);
-       }
-
-       /**
-        * {@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.getFinalRequestURI(0));
-               finished = true;
-               this.success = success;
-               synchronized (lockObject) {
-                       lockObject.notify();
-               }
-       }
-
-       //
-       // MAIN
-       //
-
-       /**
-        * Creates a new command-line interface with the given arguments.
-        *
-        * @param args
-        *            The command-line arguments
-        */
-       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
deleted file mode 100644 (file)
index f5001b0..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * jSite - Configuration.java - Copyright © 2006–2012 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 java.util.logging.Level;
-import java.util.logging.Logger;
-
-import de.todesbaum.jsite.application.FileOption;
-import de.todesbaum.jsite.application.Node;
-import de.todesbaum.jsite.application.Project;
-import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
-import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
-import de.todesbaum.util.freenet.fcp2.PriorityClass;
-import de.todesbaum.util.io.Closer;
-import de.todesbaum.util.io.StreamCopier;
-import de.todesbaum.util.xml.SimpleXML;
-import de.todesbaum.util.xml.XML;
-
-/**
- * The configuration.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Configuration {
-
-       /** The root node of the configuration. */
-       private SimpleXML rootNode;
-
-       /** The configuration locator. */
-       private final ConfigurationLocator configurationLocator;
-
-       /** Where the configuration resides. */
-       private ConfigurationLocation configurationLocation;
-
-       /**
-        * Creates a new configuration that is read from the given file.
-        *
-        * @param configurationLocator
-        *            The configuration locator
-        * @param configurationLocation
-        *            The configuration directory
-        */
-       public Configuration(ConfigurationLocator configurationLocator, ConfigurationLocation configurationLocation) {
-               this.configurationLocator = configurationLocator;
-               this.configurationLocation = configurationLocation;
-               readConfiguration(configurationLocator.getFile(configurationLocation));
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the configuration locator.
-        *
-        * @return The configuration locator
-        */
-       public ConfigurationLocator getConfigurationLocator() {
-               return configurationLocator;
-       }
-
-       /**
-        * Returns the location the configuration will be written to when calling
-        * {@link #save()}.
-        *
-        * @return The location the configuration will be written to
-        */
-       public ConfigurationLocation getConfigurationDirectory() {
-               return configurationLocation;
-       }
-
-       /**
-        * Sets the location the configuration will be written to when calling
-        * {@link #save()}.
-        *
-        * @param configurationLocation
-        *            The location to write the configuration to
-        */
-       public void setConfigurationLocation(ConfigurationLocation configurationLocation) {
-               this.configurationLocation = configurationLocation;
-       }
-
-       /**
-        * Reads the configuration from the file.
-        *
-        * @param filename
-        *            The name of the file to read the configuration from
-        */
-       private void readConfiguration(String filename) {
-               Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Reading configuration from: " + filename);
-               if (filename != null) {
-                       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) {
-                                       /* ignore. */
-                               } catch (IOException e) {
-                                       /* ignore. */
-                               } finally {
-                                       Closer.close(fileInputStream);
-                                       Closer.close(fileByteOutputStream);
-                               }
-                       }
-               }
-               rootNode = new SimpleXML("configuration");
-       }
-
-       /**
-        * Saves the configuration.
-        *
-        * @return <code>true</code> if the configuration could be saved,
-        *         <code>false</code> otherwise
-        */
-       public boolean save() {
-               Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Trying to save configuration to: " + configurationLocation);
-               File configurationFile = new File(configurationLocator.getFile(configurationLocation));
-               if (!configurationFile.exists()) {
-                       File configurationFilePath = configurationFile.getAbsoluteFile().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) {
-                       /* ignore. */
-               } finally {
-                       Closer.close(configurationInputStream);
-                       Closer.close(fileOutputStream);
-               }
-               return false;
-       }
-
-       /**
-        * Returns the value of a node.
-        *
-        * @param nodeNames
-        *            The name of all nodes in the chain
-        * @param defaultValue
-        *            The default value to return if the node could not be found
-        * @return The value of the node, or the default value if the node could not
-        *         be found
-        */
-       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();
-       }
-
-       /**
-        * Returns the integer value of a node.
-        *
-        * @param nodeNames
-        *            The names of all nodes in the chain
-        * @param defaultValue
-        *            The default value to return if the node can not be found
-        * @return The parsed integer value, or the default value if the node can
-        *         not be found or the value can not be parsed into an integer
-        */
-       private int getNodeIntValue(String[] nodeNames, int defaultValue) {
-               try {
-                       return Integer.parseInt(getNodeValue(nodeNames, String.valueOf(defaultValue)));
-               } catch (NumberFormatException nfe1) {
-                       /* ignore. */
-               }
-               return defaultValue;
-       }
-
-       /**
-        * Returns the boolean value of a node.
-        *
-        * @param nodeNames
-        *            The names of all nodes in the chain
-        * @param defaultValue
-        *            The default value to return if the node can not be found
-        * @return The parsed boolean value, or the default value if the node can
-        *         not be found
-        */
-       private boolean getNodeBooleanValue(String[] nodeNames, boolean defaultValue) {
-               String nodeValue = getNodeValue(nodeNames, null);
-               if (nodeValue == null) {
-                       return defaultValue;
-               }
-               return Boolean.parseBoolean(nodeValue);
-       }
-
-       /**
-        * Returns the hostname of the node.
-        *
-        * @return The hostname of the node
-        * @deprecated Use {@link #getSelectedNode()} instead
-        */
-       @Deprecated
-       public String getNodeAddress() {
-               return getNodeValue(new String[] { "node-address" }, "localhost");
-       }
-
-       /**
-        * Sets the hostname of the node.
-        *
-        * @param nodeAddress
-        *            The hostname of the node
-        * @deprecated Use {@link #setSelectedNode(Node)} instead
-        */
-       @Deprecated
-       public void setNodeAddress(String nodeAddress) {
-               rootNode.replace("node-address", nodeAddress);
-       }
-
-       /**
-        * The port number of the node
-        *
-        * @return The port number of the node
-        * @deprecated Use {@link #getSelectedNode()} instead.
-        */
-       @Deprecated
-       public int getNodePort() {
-               return getNodeIntValue(new String[] { "node-port" }, 9481);
-       }
-
-       /**
-        * Sets the port number of the node.
-        *
-        * @param nodePort
-        *            The port number of the node
-        * @deprecated Use {@link #setSelectedNode(Node)} instead
-        */
-       @Deprecated
-       public void setNodePort(int nodePort) {
-               rootNode.replace("node-port", String.valueOf(nodePort));
-       }
-
-       /**
-        * Returns whether the node configuration page should be skipped on startup.
-        *
-        * @return <code>true</code> to skip the node configuration page on startup,
-        *         <code>false</code> to show it
-        */
-       public boolean isSkipNodePage() {
-               return getNodeBooleanValue(new String[] { "skip-node-page" }, false);
-       }
-
-       /**
-        * Sets whether the node configuration page should be skipped on startup.
-        *
-        * @param skipNodePage
-        *            <code>true</code> to skip the node configuration page on
-        *            startup, <code>false</code> to show it
-        */
-       public void setSkipNodePage(boolean skipNodePage) {
-               rootNode.replace("skip-node-page", String.valueOf(skipNodePage));
-       }
-
-       /**
-        * Returns all configured projects.
-        *
-        * @return A list of all projects
-        */
-       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 = new Project();
-                                       projects.add(project);
-                                       project.setDescription(projectNode.getNode("description").getValue(""));
-                                       String indexFile = projectNode.getNode("index-file").getValue("");
-                                       if (indexFile.indexOf('/') > -1) {
-                                               indexFile = "";
-                                       }
-                                       project.setIndexFile(indexFile);
-                                       project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue("0")));
-                                       project.setLocalPath(projectNode.getNode("local-path").getValue(""));
-                                       project.setName(projectNode.getNode("name").getValue(""));
-                                       project.setPath(projectNode.getNode("path").getValue(""));
-                                       if ((project.getPath() != null) && (project.getPath().indexOf("/") != -1)) {
-                                               project.setPath(project.getPath().replaceAll("/", ""));
-                                       }
-                                       project.setEdition(Integer.parseInt(projectNode.getNode("edition").getValue("0")));
-                                       project.setInsertURI(projectNode.getNode("insert-uri").getValue(""));
-                                       project.setRequestURI(projectNode.getNode("request-uri").getValue(""));
-                                       if (projectNode.getNode("ignore-hidden-files") != null) {
-                                               project.setIgnoreHiddenFiles(Boolean.parseBoolean(projectNode.getNode("ignore-hidden-files").getValue("true")));
-                                       } else {
-                                               project.setIgnoreHiddenFiles(true);
-                                       }
-
-                                       /* load last insert hashes. */
-                                       Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
-                                       SimpleXML lastInsertHashesNode = projectNode.getNode("last-insert-hashes");
-                                       if (lastInsertHashesNode != null) {
-                                               for (SimpleXML fileNode : lastInsertHashesNode.getNodes("file")) {
-                                                       String filename = fileNode.getNode("filename").getValue();
-                                                       String lastInsertHash = fileNode.getNode("last-insert-hash").getValue();
-                                                       int lastInsertEdition = Integer.valueOf(fileNode.getNode("last-insert-edition").getValue());
-                                                       String lastInsertFilename = filename;
-                                                       if (fileNode.getNode("last-insert-filename") != null) {
-                                                               lastInsertFilename = fileNode.getNode("last-insert-filename").getValue();
-                                                       }
-                                                       FileOption fileOption = project.getFileOption(filename);
-                                                       fileOption.setLastInsertHash(lastInsertHash).setLastInsertEdition(lastInsertEdition).setLastInsertFilename(lastInsertFilename);
-                                                       fileOptions.put(filename, fileOption);
-                                               }
-                                       }
-
-                                       SimpleXML fileOptionsNode = projectNode.getNode("file-options");
-                                       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()));
-                                                       if (fileOptionNode.getNode("insert-redirect") != null) {
-                                                               fileOption.setInsertRedirect(Boolean.parseBoolean(fileOptionNode.getNode("insert-redirect").getValue()));
-                                                       }
-                                                       fileOption.setCustomKey(fileOptionNode.getNode("custom-key").getValue(""));
-                                                       if (fileOptionNode.getNode("changed-name") != null) {
-                                                               fileOption.setChangedName(fileOptionNode.getNode("changed-name").getValue());
-                                                       }
-                                                       fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue(""));
-                                                       fileOptions.put(filename, fileOption);
-                                               }
-                                       }
-                                       project.setFileOptions(fileOptions);
-                               } catch (NumberFormatException nfe1) {
-                                       nfe1.printStackTrace();
-                               }
-                       }
-               }
-               return projects.toArray(new Project[projects.size()]);
-       }
-
-       /**
-        * Sets the list of all projects.
-        *
-        * @param projects
-        *            The list of all projects
-        */
-       public void setProjects(Project[] projects) {
-               SimpleXML projectsNode = new SimpleXML("project-list");
-               for (Project project : projects) {
-                       SimpleXML projectNode = projectsNode.append("project");
-                       projectNode.append("edition", String.valueOf(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());
-                       projectNode.append("ignore-hidden-files", String.valueOf(project.isIgnoreHiddenFiles()));
-
-                       /* store last insert hashes. */
-                       SimpleXML lastInsertHashesNode = projectNode.append("last-insert-hashes");
-                       for (Entry<String, FileOption> fileOption : project.getFileOptions().entrySet()) {
-                               if ((fileOption.getValue().getLastInsertHash() == null) || (fileOption.getValue().getLastInsertHash().length() == 0)) {
-                                       continue;
-                               }
-                               SimpleXML fileNode = lastInsertHashesNode.append("file");
-                               fileNode.append("filename", fileOption.getKey());
-                               fileNode.append("last-insert-hash", fileOption.getValue().getLastInsertHash());
-                               fileNode.append("last-insert-edition", String.valueOf(fileOption.getValue().getLastInsertEdition()));
-                               fileNode.append("last-insert-filename", fileOption.getValue().getLastInsertFilename());
-                       }
-
-                       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("insert-redirect", String.valueOf(fileOption.isInsertRedirect()));
-                                       fileOptionNode.append("custom-key", fileOption.getCustomKey());
-                                       fileOptionNode.append("changed-name", fileOption.getChangedName());
-                                       fileOptionNode.append("mime-type", fileOption.getMimeType());
-                               }
-                       }
-               }
-               rootNode.replace(projectsNode);
-       }
-
-       /**
-        * Returns the stored locale.
-        *
-        * @return The stored locale
-        */
-       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);
-       }
-
-       /**
-        * Sets the locale to store.
-        *
-        * @param locale
-        *            The locale to store
-        */
-       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;
-       }
-
-       /**
-        * Returns a list of configured nodes.
-        *
-        * @return The list of the configured nodes
-        */
-       public Node[] getNodes() {
-               SimpleXML nodesNode = rootNode.getNode("nodes");
-               if (nodesNode == null) {
-                       String hostname = getNodeAddress();
-                       int port = getNodePort();
-                       if (hostname == null) {
-                               hostname = "127.0.0.1";
-                               port = 9481;
-                       }
-                       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;
-       }
-
-       /**
-        * Sets the list of configured nodes.
-        *
-        * @param nodes
-        *            The list of configured nodes
-        */
-       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");
-       }
-
-       /**
-        * Sets the selected node.
-        *
-        * @param selectedNode
-        *            The selected node
-        */
-       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);
-       }
-
-       /**
-        * Returns the selected node.
-        *
-        * @return The selected node
-        */
-       public Node getSelectedNode() {
-               SimpleXML selectedNodeNode = rootNode.getNode("selected-node");
-               if (selectedNodeNode == null) {
-                       String hostname = getNodeAddress();
-                       int port = getNodePort();
-                       if (hostname == null) {
-                               hostname = "127.0.0.1";
-                               port = 9481;
-                       }
-                       return new Node(hostname, port, "Node");
-               }
-               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);
-       }
-
-       /**
-        * Returns the temp directory to use.
-        *
-        * @return The temp directoy, or {@code null} to use the default temp
-        *         directory
-        */
-       public String getTempDirectory() {
-               return getNodeValue(new String[] { "temp-directory" }, null);
-       }
-
-       /**
-        * Sets the temp directory to use.
-        *
-        * @param tempDirectory
-        *            The temp directory to use, or {@code null} to use the default
-        *            temp directory
-        */
-       public void setTempDirectory(String tempDirectory) {
-               if (tempDirectory != null) {
-                       SimpleXML tempDirectoryNode = new SimpleXML("temp-directory");
-                       tempDirectoryNode.setValue(tempDirectory);
-                       rootNode.replace(tempDirectoryNode);
-               } else {
-                       rootNode.remove("temp-directory");
-               }
-       }
-
-       /**
-        * Returns whether to use the “early encode“ flag for the insert.
-        *
-        * @return {@code true} to set the “early encode” flag for the insert,
-        *         {@code false} otherwise
-        */
-       public boolean useEarlyEncode() {
-               return getNodeBooleanValue(new String[] { "use-early-encode" }, false);
-       }
-
-       /**
-        * Sets whether to use the “early encode“ flag for the insert.
-        *
-        * @param useEarlyEncode
-        *            {@code true} to set the “early encode” flag for the insert,
-        *            {@code false} otherwise
-        * @return This configuration
-        */
-       public Configuration setUseEarlyEncode(boolean useEarlyEncode) {
-               rootNode.replace("use-early-encode", String.valueOf(useEarlyEncode));
-               return this;
-       }
-
-       /**
-        * Returns the insert priority.
-        *
-        * @return The insert priority
-        */
-       public PriorityClass getPriority() {
-               return PriorityClass.valueOf(getNodeValue(new String[] { "insert-priority" }, "interactive"));
-       }
-
-       /**
-        * Sets the insert priority.
-        *
-        * @param priority
-        *            The insert priority
-        * @return This configuration
-        */
-       public Configuration setPriority(PriorityClass priority) {
-               rootNode.replace("insert-priority", priority.toString());
-               return this;
-       }
-
-       /**
-        * Returns the manifest putter.
-        *
-        * @return The manifest putter
-        */
-       public ManifestPutter getManifestPutter() {
-               return ManifestPutter.valueOf(getNodeValue(new String[] { "manifest-putter" }, "simple").toUpperCase());
-       }
-
-       /**
-        * Sets the manifest putter.
-        *
-        * @param manifestPutter
-        *            The manifest putter
-        * @return This configuration
-        */
-       public Configuration setManifestPutter(ManifestPutter manifestPutter) {
-               rootNode.replace("manifest-putter", manifestPutter.name().toLowerCase());
-               return this;
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/main/ConfigurationLocator.java b/src/de/todesbaum/jsite/main/ConfigurationLocator.java
deleted file mode 100644 (file)
index 967f936..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * jSite - ConfigurationLocator.java - Copyright © 2011–2012 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.File;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Locator for configuration files in different places.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ConfigurationLocator {
-
-       /**
-        * The location of the configuration directory.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       public enum ConfigurationLocation {
-
-               /** The configuration is in the same directory as the JAR file. */
-               NEXT_TO_JAR_FILE,
-
-               /**
-                * The configuration is in the user’s home directory. This is the
-                * pre-0.9.3 default.
-                */
-               HOME_DIRECTORY,
-
-               /** Custom location. */
-               CUSTOM,
-
-       }
-
-       /** The possible configuration locations. */
-       private final Map<ConfigurationLocation, String> configurationFiles = new HashMap<ConfigurationLocation, String>();
-
-       /**
-        * Creates a new configuration locator. If this class is loaded from a JAR
-        * file, {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is added to the list
-        * of possible configuration file locations.
-        * {@link ConfigurationLocation#HOME_DIRECTORY} is always added to this
-        * list, {@link ConfigurationLocation#CUSTOM} has to be enabled by calling
-        * {@link #setCustomLocation(String)}.
-        */
-       public ConfigurationLocator() {
-               /* are we executed from a JAR file? */
-               String resource = getClass().getResource("/" + getClass().getName().replace(".", "/") + ".class").toString();
-               if (resource.startsWith("jar:")) {
-                       String jarFileLocation = resource.substring(9, resource.indexOf(".jar!") + 4);
-                       String jarFileDirectory = new File(jarFileLocation).getParent();
-                       File configurationFile = new File(jarFileDirectory, "jSite.conf");
-                       configurationFiles.put(ConfigurationLocation.NEXT_TO_JAR_FILE, configurationFile.getPath());
-               }
-               File homeDirectoryFile = new File(System.getProperty("user.home"), ".jSite/config7");
-               configurationFiles.put(ConfigurationLocation.HOME_DIRECTORY, homeDirectoryFile.getPath());
-       }
-
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Sets the location of the custom configuration file.
-        *
-        * @param customFile
-        *            The custom location of the configuration file
-        */
-       public void setCustomLocation(String customFile) {
-               configurationFiles.put(ConfigurationLocation.CUSTOM, customFile);
-       }
-
-       /**
-        * Returns whether the given location is valid. Certain locations (such as
-        * {@link ConfigurationLocation#NEXT_TO_JAR_FILE}) may be invalid in certain
-        * circumstances (such as the application not being run from a JAR file). A
-        * location being valid does not imply that a configuration file does exist
-        * at the given location, use {@link #hasFile(ConfigurationLocation)} to
-        * check for a configuration file at the desired location.
-        *
-        * @param configurationLocation
-        *            The configuration location
-        * @return {@code true} if the location is valid, {@code false} otherwise
-        */
-       public boolean isValidLocation(ConfigurationLocation configurationLocation) {
-               return configurationFiles.containsKey(configurationLocation);
-       }
-
-       /**
-        * Checks whether a configuration file exists at the given location.
-        *
-        * @param configurationLocation
-        *            The configuration location
-        * @return {@code true} if a configuration file exists at the given
-        *         location, {@code false} otherwise
-        */
-       public boolean hasFile(ConfigurationLocation configurationLocation) {
-               if (!isValidLocation(configurationLocation)) {
-                       return false;
-               }
-               return new File(configurationFiles.get(configurationLocation)).exists();
-       }
-
-       /**
-        * Returns the configuration file for the given location.
-        *
-        * @param configurationLocation
-        *            The location to get the file for
-        * @return The name of the configuration file at the given location, or
-        *         {@code null} if the given location is invalid
-        */
-       public String getFile(ConfigurationLocation configurationLocation) {
-               return configurationFiles.get(configurationLocation);
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Finds the preferred location of the configuration file.
-        *
-        * @see #findPreferredLocation(ConfigurationLocation)
-        * @return The preferred location of the configuration file
-        */
-       public ConfigurationLocation findPreferredLocation() {
-               return findPreferredLocation(ConfigurationLocation.NEXT_TO_JAR_FILE);
-       }
-
-       /**
-        * Finds the preferred location of the configuration file. The following
-        * checks are performed: if a custom configuration location has been defined
-        * (by calling {@link #setCustomLocation(String)})
-        * {@link ConfigurationLocation#CUSTOM} is returned. If the application is
-        * run from a JAR file and a configuration file is found next to the JAR
-        * file (i.e. in the same directory),
-        * {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is returned. If a
-        * configuration file exists in the user’s home directory,
-        * {@link ConfigurationLocation#HOME_DIRECTORY} is returned. Otherwise, the
-        * given {@code defaultLocation} is returned.
-        *
-        * @param defaultLocation
-        *            The default location to return if no other configuration file
-        *            is found
-        * @return The configuration location to load the configuration from
-        */
-       public ConfigurationLocation findPreferredLocation(ConfigurationLocation defaultLocation) {
-               if (hasFile(ConfigurationLocation.CUSTOM)) {
-                       return ConfigurationLocation.CUSTOM;
-               }
-               if (hasFile(ConfigurationLocation.NEXT_TO_JAR_FILE)) {
-                       return ConfigurationLocation.NEXT_TO_JAR_FILE;
-               }
-               if (hasFile(ConfigurationLocation.HOME_DIRECTORY)) {
-                       return ConfigurationLocation.HOME_DIRECTORY;
-               }
-               if (isValidLocation(defaultLocation)) {
-                       return defaultLocation;
-               }
-               return ConfigurationLocation.HOME_DIRECTORY;
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/main/Main.java b/src/de/todesbaum/jsite/main/Main.java
deleted file mode 100644 (file)
index edbf5e4..0000000
+++ /dev/null
@@ -1,753 +0,0 @@
-/*
- * jSite - Main.java - Copyright © 2006–2012 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.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.logging.ConsoleHandler;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-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.JMenuItem;
-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.Freenet7Interface;
-import de.todesbaum.jsite.application.Node;
-import de.todesbaum.jsite.application.Project;
-import de.todesbaum.jsite.application.ProjectInserter;
-import de.todesbaum.jsite.application.ProjectInserter.CheckReport;
-import de.todesbaum.jsite.application.ProjectInserter.Issue;
-import de.todesbaum.jsite.application.UpdateChecker;
-import de.todesbaum.jsite.application.UpdateListener;
-import de.todesbaum.jsite.gui.NodeManagerListener;
-import de.todesbaum.jsite.gui.NodeManagerPage;
-import de.todesbaum.jsite.gui.PreferencesPage;
-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.jsite.i18n.I18nContainer;
-import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
-import de.todesbaum.util.image.IconLoader;
-import de.todesbaum.util.swing.TWizard;
-import de.todesbaum.util.swing.TWizardPage;
-import de.todesbaum.util.swing.WizardListener;
-
-/**
- * The main class that ties together everything.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Main implements ActionListener, ListSelectionListener, WizardListener, NodeManagerListener, UpdateListener {
-
-       /** The logger. */
-       private static final Logger logger = Logger.getLogger(Main.class.getName());
-
-       /** The version. */
-       private static final Version VERSION = new Version(0, 10);
-
-       /** The configuration. */
-       private Configuration configuration;
-
-       /** The freenet interface. */
-       private Freenet7Interface freenetInterface = new Freenet7Interface();
-
-       /** The update checker. */
-       private final UpdateChecker updateChecker;
-
-       /** The jSite icon. */
-       private Icon jSiteIcon;
-
-       /**
-        * Enumeration for all possible pages.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       private static enum PageType {
-
-               /** The node manager page. */
-               PAGE_NODE_MANAGER,
-
-               /** The project page. */
-               PAGE_PROJECTS,
-
-               /** The project files page. */
-               PAGE_PROJECT_FILES,
-
-               /** The project insert page. */
-               PAGE_INSERT_PROJECT,
-
-               /** The preferences page. */
-               PAGE_PREFERENCES
-
-       }
-
-       /** The supported locales. */
-       private static final Locale[] SUPPORTED_LOCALES = new Locale[] { Locale.ENGLISH, Locale.GERMAN, Locale.FRENCH };
-
-       /** The actions that switch the language. */
-       private Map<Locale, Action> languageActions = new HashMap<Locale, Action>();
-
-       /** The “manage nodes” action. */
-       private Action manageNodeAction;
-
-       /** The “preferences” action. */
-       private Action optionsPreferencesAction;
-
-       /** The “check for updates” action. */
-       private Action checkForUpdatesAction;
-
-       /** The “about jSite” action. */
-       private Action aboutAction;
-
-       /** The wizard. */
-       private TWizard wizard;
-
-       /** The node menu. */
-       private JMenu nodeMenu;
-
-       /** The currently selected node. */
-       private Node selectedNode;
-
-       /** Mapping from page type to page. */
-       private final Map<PageType, TWizardPage> pages = new HashMap<PageType, TWizardPage>();
-
-       /** The original location of the configuration file. */
-       private ConfigurationLocation originalLocation;
-
-       /**
-        * Creates a new core with the default configuration file.
-        */
-       private Main() {
-               this(null);
-       }
-
-       /**
-        * Creates a new core with the given configuration from the given file.
-        *
-        * @param configFilename
-        *            The name of the configuration file
-        */
-       private Main(String configFilename) {
-               /* collect all possible configuration file locations. */
-               ConfigurationLocator configurationLocator = new ConfigurationLocator();
-               if (configFilename != null) {
-                       configurationLocator.setCustomLocation(configFilename);
-               }
-
-               originalLocation = configurationLocator.findPreferredLocation();
-               logger.log(Level.CONFIG, "Using configuration from " + originalLocation + ".");
-               configuration = new Configuration(configurationLocator, originalLocation);
-
-               Locale.setDefault(configuration.getLocale());
-               I18n.setLocale(configuration.getLocale());
-               wizard = new TWizard();
-               createActions();
-               wizard.setJMenuBar(createMenuBar());
-               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);
-
-               updateChecker = new UpdateChecker(freenetInterface);
-               updateChecker.addUpdateListener(this);
-               updateChecker.start();
-
-               initPages();
-               showPage(PageType.PAGE_PROJECTS);
-       }
-
-       /**
-        * Creates all actions.
-        */
-       private void createActions() {
-               for (final Locale locale : SUPPORTED_LOCALES) {
-                       languageActions.put(locale, new AbstractAction(I18n.getMessage("jsite.menu.language." + locale.getLanguage()), IconLoader.loadIcon("/flag-" + locale.getLanguage() + ".png")) {
-
-                               @SuppressWarnings("synthetic-access")
-                               public void actionPerformed(ActionEvent actionEvent) {
-                                       switchLanguage(locale);
-                               }
-                       });
-               }
-               manageNodeAction = new AbstractAction(I18n.getMessage("jsite.menu.nodes.manage-nodes")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               showPage(PageType.PAGE_NODE_MANAGER);
-                               optionsPreferencesAction.setEnabled(true);
-                               wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
-                               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-                       }
-               };
-               optionsPreferencesAction = new AbstractAction(I18n.getMessage("jsite.menu.options.preferences")) {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               optionsPreferences();
-                       }
-               };
-               checkForUpdatesAction = new AbstractAction(I18n.getMessage("jsite.menu.help.check-for-updates")) {
-
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent actionEvent) {
-                               showLatestUpdate();
-                       }
-               };
-               aboutAction = new AbstractAction(I18n.getMessage("jsite.menu.help.about")) {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void actionPerformed(ActionEvent e) {
-                               JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.about.message"), getVersion().toString()), null, JOptionPane.INFORMATION_MESSAGE, jSiteIcon);
-                       }
-               };
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               manageNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.nodes.manage-nodes"));
-                               optionsPreferencesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.options.preferences"));
-                               checkForUpdatesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.check-for-updates"));
-                               aboutAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.about"));
-                       }
-               });
-       }
-
-       /**
-        * Creates the menu bar.
-        *
-        * @return The menu bar
-        */
-       private JMenuBar createMenuBar() {
-               JMenuBar menuBar = new JMenuBar();
-               final 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());
-
-               final JMenu optionsMenu = new JMenu(I18n.getMessage("jsite.menu.options"));
-               menuBar.add(optionsMenu);
-               optionsMenu.add(optionsPreferencesAction);
-
-               /* evil hack to right-align the help menu */
-               JPanel panel = new JPanel();
-               panel.setOpaque(false);
-               menuBar.add(panel);
-
-               final JMenu helpMenu = new JMenu(I18n.getMessage("jsite.menu.help"));
-               menuBar.add(helpMenu);
-               helpMenu.add(checkForUpdatesAction);
-               helpMenu.add(aboutAction);
-
-               I18nContainer.getInstance().registerRunnable(new Runnable() {
-
-                       @SuppressWarnings("synthetic-access")
-                       public void run() {
-                               languageMenu.setText(I18n.getMessage("jsite.menu.languages"));
-                               nodeMenu.setText(I18n.getMessage("jsite.menu.nodes"));
-                               optionsMenu.setText(I18n.getMessage("jsite.menu.options"));
-                               helpMenu.setText(I18n.getMessage("jsite.menu.help"));
-                               for (Map.Entry<Locale, Action> languageActionEntry : languageActions.entrySet()) {
-                                       languageActionEntry.getValue().putValue(Action.NAME, I18n.getMessage("jsite.menu.language." + languageActionEntry.getKey().getLanguage()));
-                               }
-                       }
-               });
-
-               return menuBar;
-       }
-
-       /**
-        * Initializes all pages.
-        */
-       private void initPages() {
-               NodeManagerPage nodeManagerPage = new NodeManagerPage(wizard);
-               nodeManagerPage.setName("page.node-manager");
-               nodeManagerPage.addNodeManagerListener(this);
-               nodeManagerPage.setNodes(configuration.getNodes());
-               pages.put(PageType.PAGE_NODE_MANAGER, nodeManagerPage);
-
-               ProjectPage projectPage = new ProjectPage(wizard);
-               projectPage.setName("page.project");
-               projectPage.setProjects(configuration.getProjects());
-               projectPage.setFreenetInterface(freenetInterface);
-               projectPage.addListSelectionListener(this);
-               pages.put(PageType.PAGE_PROJECTS, projectPage);
-
-               ProjectFilesPage projectFilesPage = new ProjectFilesPage(wizard);
-               projectFilesPage.setName("page.project.files");
-               pages.put(PageType.PAGE_PROJECT_FILES, projectFilesPage);
-
-               ProjectInsertPage projectInsertPage = new ProjectInsertPage(wizard);
-               projectInsertPage.setName("page.project.insert");
-               projectInsertPage.setFreenetInterface(freenetInterface);
-               pages.put(PageType.PAGE_INSERT_PROJECT, projectInsertPage);
-
-               PreferencesPage preferencesPage = new PreferencesPage(wizard);
-               preferencesPage.setName("page.preferences");
-               preferencesPage.setTempDirectory(configuration.getTempDirectory());
-               pages.put(PageType.PAGE_PREFERENCES, preferencesPage);
-       }
-
-       /**
-        * Shows the page with the given type.
-        *
-        * @param pageType
-        *            The page type to show
-        */
-       private 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");
-       }
-
-       /**
-        * Returns whether a configuration file would be overwritten when calling
-        * {@link #saveConfiguration()}.
-        *
-        * @return {@code true} if {@link #saveConfiguration()} would overwrite an
-        *         existing file, {@code false} otherwise
-        */
-       private boolean isOverwritingConfiguration() {
-               return configuration.getConfigurationLocator().hasFile(configuration.getConfigurationDirectory());
-       }
-
-       /**
-        * Saves the configuration.
-        *
-        * @return <code>true</code> if the configuration could be saved,
-        *         <code>false</code> otherwise
-        */
-       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());
-
-               PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES);
-               configuration.setTempDirectory(preferencesPage.getTempDirectory());
-
-               return configuration.save();
-       }
-
-       /**
-        * Finds a supported locale for the given locale.
-        *
-        * @param forLocale
-        *            The locale to find a supported locale for
-        * @return The supported locale that was found, or the default locale if no
-        *         supported locale could be found
-        */
-       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];
-       }
-
-       /**
-        * Returns the version.
-        *
-        * @return The version
-        */
-       public static final Version getVersion() {
-               return VERSION;
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Switches the language of the interface to the given locale.
-        *
-        * @param locale
-        *            The locale to switch to
-        */
-       private void switchLanguage(Locale locale) {
-               Locale supportedLocale = findSupportedLocale(locale);
-               Action languageAction = languageActions.get(supportedLocale);
-               JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) languageAction.getValue("menuItem");
-               menuItem.setSelected(true);
-               I18n.setLocale(supportedLocale);
-               for (Runnable i18nRunnable : I18nContainer.getInstance()) {
-                       try {
-                               i18nRunnable.run();
-                       } catch (Throwable t) {
-                               /* we probably shouldn't swallow this. */
-                       }
-               }
-               wizard.setPage(wizard.getPage());
-               configuration.setLocale(supportedLocale);
-       }
-
-       /**
-        * Shows a dialog with general preferences.
-        */
-       private void optionsPreferences() {
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setConfigurationLocation(configuration.getConfigurationDirectory());
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasNextToJarConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.NEXT_TO_JAR_FILE));
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasCustomConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.CUSTOM));
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setUseEarlyEncode(configuration.useEarlyEncode());
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setPriority(configuration.getPriority());
-               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setManifestPutter(configuration.getManifestPutter());
-               showPage(PageType.PAGE_PREFERENCES);
-               optionsPreferencesAction.setEnabled(false);
-               wizard.setNextEnabled(true);
-               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
-       }
-
-       /**
-        * Shows a dialog box that shows the last version that was found by the
-        * {@link UpdateChecker}.
-        */
-       private void showLatestUpdate() {
-               Version latestVersion = updateChecker.getLatestVersion();
-               int versionDifference = latestVersion.compareTo(VERSION);
-               if (versionDifference > 0) {
-                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.newer.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
-               } else if (versionDifference < 0) {
-                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.older.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
-               } else {
-                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.okay.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
-               }
-       }
-
-       //
-       // 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);
-               } 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.warning.no-local-path"), null, JOptionPane.ERROR_MESSAGE);
-                               return;
-                       }
-                       if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
-                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.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);
-               } 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.error.no-node-selected"), null, JOptionPane.ERROR_MESSAGE);
-                               return;
-                       }
-                       CheckReport checkReport = ProjectInserter.validateProject(project);
-                       for (Issue issue : checkReport) {
-                               if (issue.isFatal()) {
-                                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.ERROR_MESSAGE);
-                                       return;
-                               }
-                               if (JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
-                                       return;
-                               }
-                       }
-                       boolean nodeRunning = false;
-                       try {
-                               nodeRunning = freenetInterface.isNodePresent();
-                       } catch (IOException e) {
-                               /* ignore. */
-                       }
-                       if (!nodeRunning) {
-                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-running"), null, JOptionPane.ERROR_MESSAGE);
-                               return;
-                       }
-                       configuration.save();
-                       showPage(PageType.PAGE_INSERT_PROJECT);
-                       ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT);
-                       String tempDirectory = ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).getTempDirectory();
-                       projectInsertPage.setTempDirectory(tempDirectory);
-                       projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode());
-                       projectInsertPage.setPriority(configuration.getPriority());
-                       projectInsertPage.setManifestPutter(configuration.getManifestPutter());
-                       projectInsertPage.startInsert();
-                       nodeMenu.setEnabled(false);
-                       optionsPreferencesAction.setEnabled(false);
-               } else if ("page.project.insert".equals(pageName)) {
-                       ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT);
-                       if (projectInsertPage.isRunning()) {
-                               projectInsertPage.stopInsert();
-                       } else {
-                               showPage(PageType.PAGE_PROJECTS);
-                               nodeMenu.setEnabled(true);
-                               optionsPreferencesAction.setEnabled(true);
-                       }
-               } else if ("page.preferences".equals(pageName)) {
-                       PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES);
-                       showPage(PageType.PAGE_PROJECTS);
-                       optionsPreferencesAction.setEnabled(true);
-                       configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode());
-                       configuration.setPriority(preferencesPage.getPriority());
-                       configuration.setManifestPutter(preferencesPage.getManifestPutter());
-                       configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation());
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void wizardPreviousPressed(TWizard wizard) {
-               String pageName = wizard.getPage().getName();
-               if ("page.project".equals(pageName) || "page.preferences".equals(pageName)) {
-                       showPage(PageType.PAGE_NODE_MANAGER);
-                       optionsPreferencesAction.setEnabled(true);
-               } else if ("page.project.files".equals(pageName)) {
-                       showPage(PageType.PAGE_PROJECTS);
-               } else if ("page.project.insert".equals(pageName)) {
-                       showPage(PageType.PAGE_PROJECT_FILES);
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void wizardQuitPressed(TWizard wizard) {
-               if (((ProjectPage) pages.get(PageType.PAGE_PROJECTS)).wasUriCopied() || ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).wasUriCopied()) {
-                       JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.use-clipboard-now"));
-               }
-               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), I18n.getMessage("jsite.quit.question.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
-                       if (isOverwritingConfiguration() && !originalLocation.equals(configuration.getConfigurationDirectory())) {
-                               int overwriteConfigurationAnswer = JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.quit.overwrite-configuration"), configuration.getConfigurationLocator().getFile(configuration.getConfigurationDirectory())), I18n.getMessage("jsite.quit.overwrite-configuration.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
-                               if (overwriteConfigurationAnswer == JOptionPane.YES_OPTION) {
-                                       if (saveConfiguration()) {
-                                               System.exit(0);
-                                       }
-                               } else if (overwriteConfigurationAnswer == JOptionPane.CANCEL_OPTION) {
-                                       return;
-                               }
-                               if (overwriteConfigurationAnswer == JOptionPane.NO_OPTION) {
-                                       System.exit(0);
-                               }
-                       } else {
-                               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 nodeSelected(Node node) {
-               for (Component menuItem : nodeMenu.getMenuComponents()) {
-                       if (menuItem instanceof JMenuItem) {
-                               if (node.equals(((JMenuItem) menuItem).getClientProperty("Node"))) {
-                                       ((JMenuItem) menuItem).setSelected(true);
-                               }
-                       }
-               }
-               freenetInterface.setNode(node);
-               selectedNode = node;
-       }
-
-       //
-       // INTERFACE ActionListener
-       //
-
-       /**
-        * {@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);
-               }
-       }
-
-       //
-       // INTERFACE UpdateListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void foundUpdateData(Version foundVersion, long versionTimestamp) {
-               logger.log(Level.FINEST, "Found version {0} from {1,date}.", new Object[] { foundVersion, versionTimestamp });
-               if (foundVersion.compareTo(VERSION) > 0) {
-                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.found-version.message"), foundVersion.toString(), new Date(versionTimestamp)), I18n.getMessage("jsite.update-checker.found-version.title"), JOptionPane.INFORMATION_MESSAGE);
-               }
-       }
-
-       //
-       // MAIN METHOD
-       //
-
-       /**
-        * Main method that is called by the VM.
-        *
-        * @param args
-        *            The command-line arguments
-        */
-       public static void main(String[] args) {
-               /* initialize logger. */
-               Logger logger = Logger.getLogger("de.todesbaum");
-               Handler handler = new ConsoleHandler();
-               logger.addHandler(handler);
-               String configFilename = null;
-               boolean nextIsConfigFilename = false;
-               for (String argument : args) {
-                       if (nextIsConfigFilename) {
-                               configFilename = argument;
-                               nextIsConfigFilename = false;
-                       }
-                       if ("--help".equals(argument)) {
-                               printHelp();
-                               return;
-                       } else if ("--debug".equals(argument)) {
-                               logger.setLevel(Level.ALL);
-                               handler.setLevel(Level.ALL);
-                       } else if ("--config-file".equals(argument)) {
-                               nextIsConfigFilename = true;
-                       }
-               }
-               if (nextIsConfigFilename) {
-                       System.out.println("--config-file needs parameter!");
-                       return;
-               }
-               new Main(configFilename);
-       }
-
-       /**
-        * Prints a small syntax help.
-        */
-       private static void printHelp() {
-               System.out.println("--help\tshows this cruft");
-               System.out.println("--debug\tenables some debug output");
-               System.out.println("--config-file <file>\tuse specified configuration file");
-       }
-
-}
diff --git a/src/de/todesbaum/jsite/main/Version.java b/src/de/todesbaum/jsite/main/Version.java
deleted file mode 100644 (file)
index 977836c..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * jSite - Version.java - Copyright © 2006–2012 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;
-
-/**
- * Container for version information.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public class Version implements Comparable<Version> {
-
-       /** The components of the version information. */
-       private final int[] components;
-
-       /**
-        * Creates a new version container with the given components.
-        *
-        * @param components
-        *            The version components
-        */
-       public Version(int... components) {
-               this.components = new int[components.length];
-               System.arraycopy(components, 0, this.components, 0, components.length);
-       }
-
-       /**
-        * Returns the number of version components.
-        *
-        * @return The number of version components
-        */
-       public int size() {
-               return components.length;
-       }
-
-       /**
-        * Returns the version component with the given index.
-        *
-        * @param index
-        *            The index of the version component
-        * @return The version component
-        */
-       public int getComponent(int index) {
-               return components[index];
-       }
-
-       /**
-        * Parses a version from the given string.
-        *
-        * @param versionString
-        *            The version string to parse
-        * @return The parsed version, or <code>null</code> if the string could not
-        *         be parsed
-        */
-       public static Version parse(String versionString) {
-               String[] componentStrings = versionString.split("\\.");
-               int[] components = new int[componentStrings.length];
-               int index = -1;
-               for (String componentString : componentStrings) {
-                       try {
-                               components[++index] = Integer.parseInt(componentString);
-                       } catch (NumberFormatException nfe1) {
-                               return null;
-                       }
-               }
-               return new Version(components);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public String toString() {
-               StringBuilder versionString = new StringBuilder();
-               for (int component : components) {
-                       if (versionString.length() != 0) {
-                               versionString.append('.');
-                       }
-                       versionString.append(component);
-               }
-               return versionString.toString();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public int compareTo(Version version) {
-               int lessComponents = Math.min(components.length, version.components.length);
-               for (int index = 0; index < lessComponents; index++) {
-                       if (version.components[index] == components[index]) {
-                               continue;
-                       }
-                       return components[index] - version.components[index];
-               }
-               return components.length - version.components.length;
-       }
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Client.java b/src/de/todesbaum/util/freenet/fcp2/Client.java
deleted file mode 100644 (file)
index ede92d1..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * jSite - Client.java - Copyright © 2006–2012 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;
-
-import de.todesbaum.util.io.StreamCopier.ProgressListener;
-
-/**
- * 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$
- */
-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. 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
-        * @param progressListener
-        *            The progress listener for payload transfers
-        * @throws IOException
-        *             if an I/O error occurs
-        * @see #execute(Command, boolean)
-        */
-       public void execute(Command command, ProgressListener progressListener) throws IOException {
-               execute(command, true, progressListener);
-       }
-
-       /**
-        * 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 {
-               execute(command, removeExistingIdentifiers, null);
-       }
-
-       /**
-        * 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
-        * @param progressListener
-        *            The progress listener for payload transfers
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       public void execute(Command command, boolean removeExistingIdentifiers, ProgressListener progressListener) throws IOException {
-               synchronized (messageQueue) {
-                       messageQueue.clear();
-                       if (removeExistingIdentifiers) {
-                               identifiers.clear();
-                       }
-                       identifiers.add(command.getIdentifier());
-               }
-               connection.execute(command, progressListener);
-       }
-
-       /**
-        * 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/ClientGet.java b/src/de/todesbaum/util/freenet/fcp2/ClientGet.java
deleted file mode 100644 (file)
index 5b340e4..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * jSite - ClientGet.java - Copyright © 2008–2012 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 “ClientGet” command.
- *
- * @author David ‘BombeB Roden &lt;bombe@freenetproject.org&gt;
- */
-public class ClientGet extends Command {
-
-       private boolean ignoreDataStore;
-       private boolean dataStoreOnly;
-       private String uri;
-       private Verbosity verbosity = Verbosity.NONE;
-       private long maxSize = -1;
-       private long maxTempSize = -1;
-       private int maxRetries = -1;
-       private PriorityClass priorityClass = PriorityClass.INTERACTIVE;
-       private Persistence persistence = Persistence.CONNECTION;
-       private String clientToken;
-       private boolean global = false;
-       private ReturnType returnType = ReturnType.direct;
-       private boolean binaryBlob = false;
-       private String allowedMimeTypes = null;
-       private String filename = null;
-       private String tempFilename = null;
-
-       /**
-        *Creates a new ClientGet command with the given request identifier.
-        *
-        * @param identifier
-        *            The request identifier
-        */
-       public ClientGet(String identifier) {
-               super("ClientGet", identifier);
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public boolean isIgnoreDataStore() {
-               return ignoreDataStore;
-       }
-
-       /**
-        * TODO
-        *
-        * @param ignoreDataStore
-        */
-       public void setIgnoreDataStore(boolean ignoreDataStore) {
-               this.ignoreDataStore = ignoreDataStore;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public boolean isDataStoreOnly() {
-               return dataStoreOnly;
-       }
-
-       /**
-        * TODO
-        *
-        * @param dataStoreOnly
-        */
-       public void setDataStoreOnly(boolean dataStoreOnly) {
-               this.dataStoreOnly = dataStoreOnly;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public String getUri() {
-               return uri;
-       }
-
-       /**
-        * TODO
-        *
-        * @param uri
-        */
-       public void setUri(String uri) {
-               this.uri = uri;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public Verbosity getVerbosity() {
-               return verbosity;
-       }
-
-       /**
-        * TODO
-        *
-        * @param verbosity
-        */
-       public void setVerbosity(Verbosity verbosity) {
-               this.verbosity = verbosity;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public long getMaxSize() {
-               return maxSize;
-       }
-
-       /**
-        * TODO
-        *
-        * @param maxSize
-        */
-       public void setMaxSize(long maxSize) {
-               this.maxSize = maxSize;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public long getMaxTempSize() {
-               return maxTempSize;
-       }
-
-       /**
-        * TODO
-        *
-        * @param maxTempSize
-        */
-       public void setMaxTempSize(long maxTempSize) {
-               this.maxTempSize = maxTempSize;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public int getMaxRetries() {
-               return maxRetries;
-       }
-
-       /**
-        * TODO
-        *
-        * @param maxRetries
-        */
-       public void setMaxRetries(int maxRetries) {
-               this.maxRetries = maxRetries;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public PriorityClass getPriorityClass() {
-               return priorityClass;
-       }
-
-       /**
-        * TODO
-        *
-        * @param priorityClass
-        */
-       public void setPriorityClass(PriorityClass priorityClass) {
-               this.priorityClass = priorityClass;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public Persistence getPersistence() {
-               return persistence;
-       }
-
-       /**
-        * TODO
-        *
-        * @param persistence
-        */
-       public void setPersistence(Persistence persistence) {
-               this.persistence = persistence;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public String getClientToken() {
-               return clientToken;
-       }
-
-       /**
-        * TODO
-        *
-        * @param clientToken
-        */
-       public void setClientToken(String clientToken) {
-               this.clientToken = clientToken;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public boolean isGlobal() {
-               return global;
-       }
-
-       /**
-        * TODO
-        *
-        * @param global
-        */
-       public void setGlobal(boolean global) {
-               this.global = global;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public ReturnType getReturnType() {
-               return returnType;
-       }
-
-       /**
-        * TODO
-        *
-        * @param returnType
-        */
-       public void setReturnType(ReturnType returnType) {
-               this.returnType = returnType;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public boolean isBinaryBlob() {
-               return binaryBlob;
-       }
-
-       /**
-        * TODO
-        *
-        * @param binaryBlob
-        */
-       public void setBinaryBlob(boolean binaryBlob) {
-               this.binaryBlob = binaryBlob;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public String getAllowedMimeTypes() {
-               return allowedMimeTypes;
-       }
-
-       /**
-        * TODO
-        *
-        * @param allowedMimeTypes
-        */
-       public void setAllowedMimeTypes(String allowedMimeTypes) {
-               this.allowedMimeTypes = allowedMimeTypes;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public String getFilename() {
-               return filename;
-       }
-
-       /**
-        * TODO
-        *
-        * @param filename
-        */
-       public void setFilename(String filename) {
-               this.filename = filename;
-       }
-
-       /**
-        * TODO
-        *
-        * @return
-        */
-       public String getTempFilename() {
-               return tempFilename;
-       }
-
-       /**
-        * TODO
-        *
-        * @param tempFilename
-        */
-       public void setTempFilename(String tempFilename) {
-               this.tempFilename = tempFilename;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       protected void write(Writer writer) throws IOException {
-               super.write(writer);
-               writer.write("IgnoreDS=" + ignoreDataStore + LINEFEED);
-               writer.write("DSonly=" + dataStoreOnly + LINEFEED);
-               writer.write("URI=" + uri + LINEFEED);
-               writer.write("Verbosity=" + verbosity.getValue() + LINEFEED);
-               if (maxSize > -1) {
-                       writer.write("MaxSize=" + maxSize + LINEFEED);
-               }
-               if (maxTempSize > -1) {
-                       writer.write("MaxTempSize=" + maxTempSize + LINEFEED);
-               }
-               if (maxRetries >= -1) {
-                       writer.write("MaxRetries=" + maxRetries + LINEFEED);
-               }
-               writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED);
-               writer.write("Persistence=" + persistence.getName() + LINEFEED);
-               if (clientToken != null) {
-                       writer.write("ClientToken=" + clientToken + LINEFEED);
-               }
-               writer.write("Global=" + global + LINEFEED);
-               writer.write("BinaryBlob=" + binaryBlob + LINEFEED);
-               if (allowedMimeTypes != null) {
-                       writer.write("AllowedMIMETypes=" + allowedMimeTypes + LINEFEED);
-               }
-               if (returnType == ReturnType.disk) {
-                       writer.write("Filename=" + filename + LINEFEED);
-                       if (tempFilename != null) {
-                               writer.write("TempFilename=" + tempFilename + LINEFEED);
-                       }
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/de/todesbaum/util/freenet/fcp2/ClientHello.java
deleted file mode 100644 (file)
index 5983078..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * jSite - ClientHello.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 9993173..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * jSite - ClientPut.java - Copyright © 2006–2012 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$
- */
-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;
-
-       /** Whether to generate the keys early. */
-       protected boolean earlyEncode = false;
-
-       /** 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 whether the data should be encoded early to generate the final
-        * key as fast as possible.
-        *
-        * @return {@code true} if the key should be generated early, {@code false}
-        *         otherwise
-        */
-       public boolean isEarlyEncode() {
-               return earlyEncode;
-       }
-
-       /**
-        * Sets whether the data should be encoded early to generate the final key
-        * as fast as possible.
-        *
-        * @param earlyEncode
-        *            {@code true} if the key should be generated early, {@code
-        *            false} otherwise
-        */
-       public void setEarlyEncode(boolean earlyEncode) {
-               this.earlyEncode = earlyEncode;
-       }
-
-       /**
-        * 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);
-               writer.write("EarlyEncode=" + earlyEncode);
-               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
deleted file mode 100644 (file)
index d72f2c1..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * jSite - ClientPutComplexDir.java - Copyright © 2006–2012 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.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.List;
-
-import de.todesbaum.util.io.Closer;
-
-/**
- * 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$
- */
-public class ClientPutComplexDir extends ClientPutDir<ClientPutComplexDir> {
-
-       /** 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 File payloadFile;
-
-       /** The total number of bytes of the payload. */
-       private long payloadLength = 0;
-
-       /** The temp directory to use. */
-       private final String tempDirectory;
-
-       /**
-        * 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) {
-               this(identifier, uri, null);
-       }
-
-       /**
-        * 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
-        * @param tempDirectory
-        *            The temp directory to use, or {@code null} to use the default
-        *            temp directory
-        */
-       public ClientPutComplexDir(String identifier, String uri, String tempDirectory) {
-               super("ClientPutComplexDir", identifier, uri);
-               this.tempDirectory = tempDirectory;
-       }
-
-       /**
-        * Adds a file to the directory inserted by this request.
-        *
-        * @param fileEntry
-        *            The file entry to add to the directory
-        * @throws IOException
-        *             if an I/O error occurs when creating the payload stream
-        */
-       public void addFileEntry(FileEntry fileEntry) throws IOException {
-               if (fileEntry instanceof DirectFileEntry) {
-                       if (payloadFile == null) {
-                               try {
-                                       payloadFile = File.createTempFile("payload", ".dat", (tempDirectory != null) ? new File(tempDirectory) : null);
-                                       payloadFile.deleteOnExit();
-                               } catch (IOException e) {
-                                       /* ignore. */
-                               }
-                       }
-                       if (payloadFile != null) {
-                               InputStream payloadInputStream = ((DirectFileEntry) fileEntry).getDataInputStream();
-                               FileOutputStream payloadOutputStream = null;
-                               try {
-                                       payloadOutputStream = new FileOutputStream(payloadFile, true);
-                                       byte[] buffer = new byte[65536];
-                                       int read = 0;
-                                       while ((read = payloadInputStream.read(buffer)) != -1) {
-                                               payloadOutputStream.write(buffer, 0, read);
-                                       }
-                                       payloadOutputStream.flush();
-                                       fileEntries.add(fileEntry);
-                               } catch (IOException ioe1) {
-                                       payloadFile.delete();
-                                       throw ioe1;
-                               } finally {
-                                       Closer.close(payloadOutputStream);
-                                       Closer.close(payloadInputStream);
-                               }
-                       }
-               } else {
-                       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();
-                       } 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() {
-               if (payloadFile != null) {
-                       try {
-                               return new FileInputStream(payloadFile);
-                       } catch (FileNotFoundException e) {
-                               /* shouldn't occur. */
-                       }
-               }
-               return null;
-       }
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java
deleted file mode 100644 (file)
index 386fe1d..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * jSite - ClientPutDir.java - Copyright © 2006–2012 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.
- *
- * @param <C>
- *            The type of the “ClientPutDir” command
- * @author David Roden &lt;droden@gmail.com&gt;
- */
-public class ClientPutDir<C extends ClientPutDir<?>> extends ClientPut {
-
-       /**
-        * All possible manifest putters. Manifest putters are used to distribute
-        * files of a directory insert to different containers, depending on size,
-        * type, and other factors.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       public enum ManifestPutter {
-
-               /**
-                * Use the “simple” manifest putter. Despite its name this is currently
-                * the default manifest putter.
-                */
-               SIMPLE("simple"),
-
-               /** Use the “default” manifest putter. */
-               DEFAULT("default");
-
-               /** The name of the manifest putter. */
-               private final String name;
-
-               /**
-                * Creates a new manifest putter.
-                *
-                * @param name
-                *            The name of the manifest putter
-                */
-               private ManifestPutter(String name) {
-                       this.name = name;
-               }
-
-               /**
-                * Returns the name of the manifest putter.
-                *
-                * @return The name of the manifest putter
-                */
-               public String getName() {
-                       return name;
-               }
-
-               //
-               // OBJECT METHODS
-               //
-
-               /**
-                * {@inheritDoc}
-                */
-               @Override
-               public String toString() {
-                       return name.substring(0, 1).toUpperCase() + name.substring(1);
-               }
-
-       }
-
-       /** The default file of the directory. */
-       protected String defaultName;
-
-       /** The manifest putter to use. */
-       private ManifestPutter manifestPutter;
-
-       /**
-        * 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;
-       }
-
-       /**
-        * Returns the current manifest putter.
-        *
-        * @return The current manifest putter (may be {@code null})
-        */
-       public ManifestPutter getManifestPutter() {
-               return manifestPutter;
-       }
-
-       /**
-        * Sets the manifest putter for the “ClientPutDir” command. If {@code null}
-        * is given the node will choose a manifest putter.
-        *
-        * @param manifestPutter
-        *            The manifest putter to use for the command (may be
-        *            {@code null})
-        * @return This ClientPutDir command
-        */
-       @SuppressWarnings("unchecked")
-       public C setManifestPutter(ManifestPutter manifestPutter) {
-               this.manifestPutter = manifestPutter;
-               return (C) this;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       protected void write(Writer writer) throws IOException {
-               super.write(writer);
-               if (defaultName != null)
-                       writer.write("DefaultName=" + defaultName + LINEFEED);
-               if (manifestPutter != null) {
-                       writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED);
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Command.java b/src/de/todesbaum/util/freenet/fcp2/Command.java
deleted file mode 100644 (file)
index 63aaa9c..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * jSite - Command.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index c4cb669..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * jSite - Connection.java - Copyright © 2006–2012 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.Closer;
-import de.todesbaum.util.io.LineInputStream;
-import de.todesbaum.util.io.StreamCopier;
-import de.todesbaum.util.io.StreamCopier.ProgressListener;
-import de.todesbaum.util.io.TempFileInputStream;
-
-/**
- * A physical connection to a Freenet node.
- *
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- */
-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;
-
-       /** The temp directory to use. */
-       private String tempDirectory;
-
-       /**
-        * 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;
-       }
-
-       /**
-        * Sets the temp directory to use for creation of temporary files.
-        *
-        * @param tempDirectory
-        *            The temp directory to use, or {@code null} to use the default
-        *            temp directory
-        */
-       public void setTempDirectory(String tempDirectory) {
-               this.tempDirectory = tempDirectory;
-       }
-
-       /**
-        * 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 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() {
-               Closer.close(nodeWriter);
-               nodeWriter = null;
-               Closer.close(nodeOutputStream);
-               nodeOutputStream = null;
-               Closer.close(nodeInputStream);
-               nodeInputStream = null;
-               nodeInputStream = null;
-               Closer.close(nodeSocket);
-               nodeSocket = null;
-               synchronized (this) {
-                       notify();
-               }
-               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 {
-               execute(command, null);
-       }
-
-       /**
-        * Executes the specified command.
-        *
-        * @param command
-        *            The command to execute
-        * @param progressListener
-        *            A progress listener for a payload transfer
-        * @throws IllegalStateException
-        *             if the connection is not connected
-        * @throws IOException
-        *             if an I/O error occurs
-        */
-       public synchronized void execute(Command command, ProgressListener progressListener) 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()) {
-                       InputStream payloadInputStream = null;
-                       try {
-                               payloadInputStream = command.getPayload();
-                               StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength(), progressListener);
-                       } finally {
-                               Closer.close(payloadInputStream);
-                       }
-                       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$
-        */
-       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.
-                */
-               @SuppressWarnings("synthetic-access")
-               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", (tempDirectory != null) ? new File(tempDirectory) : null);
-                                                       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
deleted file mode 100644 (file)
index 8e55a2d..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * jSite - ConnectionListener.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 9b12e74..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * jSite - DirectFileEntry.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index bf036b4..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * jSite - DiskFileEntry.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 7adb11f..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * jSite - FileEntry.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 84e4bf6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * jSite - GenerateSSK.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 6c934ec..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * jSite - Message.java - Copyright © 2006–2012 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.Map.Entry;
-import java.util.Set;
-
-/**
- * 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$
- * @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
-        */
-       @Override
-       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
deleted file mode 100644 (file)
index d2fa91c..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * jSite - Node.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 55e73f8..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * jSite - Persistence.java - Copyright © 2006–2012 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;
-
-/**
- * The possible persistence options. This specify whether (and for how long) the
- * node remembers to execute a request and the results. Possible values are
- * <code>connection</code>, <code>reboot</code>, and <code>forever</code>.
- * <code>connection</code> means that a request is aborted as soon as the
- * connection to the node is severed. <code>reboot</code> means that a request
- * is remembered as long as the node is running but not after restarts.
- * <code>forever</code> finally means that a request persists until it is
- * explicitely deleted.
- *
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- * @see de.todesbaum.util.freenet.fcp2.ModifyPersistentRequest
- * @see de.todesbaum.util.freenet.fcp2.RemovePersistentRequest
- */
-public final class Persistence {
-
-       /**
-        * Denotes that a request should be terminated if the connection to the node
-        * is severed.
-        */
-       public static final Persistence CONNECTION = new Persistence("connection");
-
-       /** Denotes that a request should be remembered until the node is restarted. */
-       public static final Persistence REBOOT = new Persistence("reboot");
-
-       /**
-        * Denotes that a request should be remembered until it is explicitely
-        * deleted.
-        */
-       public static final Persistence FOREVER = new Persistence("forever");
-
-       /** The name of this persistence option. */
-       private String name;
-
-       /**
-        * Private constructor that creates a persistence option with the specified
-        * name.
-        *
-        * @param name
-        *            The name of the persistence option.
-        */
-       private Persistence(String name) {
-               this.name = name;
-       }
-
-       /**
-        * Returns the name of this persistence option.
-        *
-        * @return The name of this persistence option
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Returns a textual representation of this persistence option. The result
-        * is identical to calling {@link #getName()}.
-        *
-        * @return The name of this persistence option
-        */
-       @Override
-       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
deleted file mode 100644 (file)
index 623bc5b..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * jSite - PriorityClass.java - Copyright © 2006–2012 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;
-
-/**
- * The possible priority classes. Possible values are, in order of descending
- * priority: <code>maximum</code> (anything more important than fproxy),
- * <code>interactive</code> (fproxy), <code>semi-interactive</code> (fproxy
- * immediate mode large file downloads, not to disk), <code>updatable</code>
- * (updatable site checks), <code>bulk</code> (large file downloads to disk),
- * <code>prefetch</code>, <code>minimum</code>.
- *
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- */
-public final class PriorityClass {
-
-       /** Denotes <code>maximum</code> priority class. */
-       public static final PriorityClass MAXIMUM = new PriorityClass("maximum", 0);
-
-       /** Denotes <code>interactive</code> priority class. */
-       public static final PriorityClass INTERACTIVE = new PriorityClass("interactive", 1);
-
-       /** Denotes <code>semi-interactive</code> priority class. */
-       public static final PriorityClass SEMI_INTERACTIVE = new PriorityClass("semiInteractive", 2);
-
-       /** Denotes <code>updatable</code> priority class. */
-       public static final PriorityClass UPDATABLE = new PriorityClass("updatable", 3);
-
-       /** Denotes <code>bulk</code> priority class. */
-       public static final PriorityClass BULK = new PriorityClass("bulk", 4);
-
-       /** Denotes <code>prefetch</code> priority class. */
-       public static final PriorityClass PREFETCH = new PriorityClass("prefetch", 5);
-
-       /** Denotes <code>minimum</code> priority class. */
-       public static final PriorityClass MINIMUM = new PriorityClass("minimum", 6);
-
-       /** The name of the priority class. */
-       private String name;
-
-       /** The value of the priority class. */
-       private int value;
-
-       /**
-        * Creates a new priority class with the specified name and value.
-        *
-        * @param name
-        *            The name of the priority class
-        * @param value
-        *            The value of the priority class
-        */
-       private PriorityClass(String name, int value) {
-               this.name = name;
-               this.value = value;
-       }
-
-       /**
-        * Returns the name of this priority class.
-        *
-        * @return The name of this priority class
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
-        * Returns the value of this priority class.
-        *
-        * @return The value of this priority class
-        */
-       public int getValue() {
-               return value;
-       }
-
-       //
-       // STATIC METHODS
-       //
-
-       /**
-        * Returns the priority class with the given name, matched case-insensitive.
-        *
-        * @param value
-        *            The name of the priority
-        * @return The priority with the given name, or {@code null} if no priority
-        *         matches the given name
-        */
-       public static PriorityClass valueOf(String value) {
-               for (PriorityClass priorityClass : new PriorityClass[] { MINIMUM, PREFETCH, BULK, UPDATABLE, SEMI_INTERACTIVE, INTERACTIVE, MAXIMUM }) {
-                       if (priorityClass.getName().equalsIgnoreCase(value)) {
-                               return priorityClass;
-                       }
-               }
-               return null;
-       }
-
-       //
-       // OBJECT METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public String toString() {
-               return name;
-       }
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java
deleted file mode 100644 (file)
index 06a2171..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * jSite - RedirectFileEntry.java - Copyright © 2006–2012 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/ReturnType.java b/src/de/todesbaum/util/freenet/fcp2/ReturnType.java
deleted file mode 100644 (file)
index 6f2ed7a..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * jSite - ReturnType.java - Copyright © 2008–2012 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;
-
-/**
- * Enumeration for the different return types a {@link ClientGet} request can
- * have.
- *
- * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
- */
-public enum ReturnType {
-
-       /** The data is returned as payload. */
-       direct,
-
-       /** The data is written to disk. */
-       disk,
-
-       /** The data is not returned at all. */
-       none
-
-}
diff --git a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/de/todesbaum/util/freenet/fcp2/Verbosity.java
deleted file mode 100644 (file)
index e59b6a3..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * jSite - Verbosity.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index ff6441a..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * jSite - IconLoader.java - Copyright © 2006–2012 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.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$
- */
-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/Closer.java b/src/de/todesbaum/util/io/Closer.java
deleted file mode 100644 (file)
index 67f7c57..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * jSite - Closer.java - Copyright © 2006–2012 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.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.Writer;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-
-/**
- * Helper class that can close all kinds of resources without throwing exception
- * so that clean-up code can be written with less code. All methods check that
- * the given resource is not <code>null</code> before invoking the close()
- * method of the respective type.
- *
- * @author <a href="mailto:bombe@freenetproject.org">David &lsquo;Bombe&squo;
- *         Roden</a>
- * @version $Id$
- */
-public class Closer {
-
-       /**
-        * Closes the given result set.
-        *
-        * @param resultSet
-        *            The result set to close
-        * @see ResultSet#close()
-        */
-       public static void close(ResultSet resultSet) {
-               if (resultSet != null) {
-                       try {
-                               resultSet.close();
-                       } catch (SQLException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given statement.
-        *
-        * @param statement
-        *            The statement to close
-        * @see Statement#close()
-        */
-       public static void close(Statement statement) {
-               if (statement != null) {
-                       try {
-                               statement.close();
-                       } catch (SQLException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given connection.
-        *
-        * @param connection
-        *            The connection to close
-        * @see Connection#close()
-        */
-       public static void close(Connection connection) {
-               if (connection != null) {
-                       try {
-                               connection.close();
-                       } catch (SQLException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given server socket.
-        *
-        * @param serverSocket
-        *            The server socket to close
-        * @see ServerSocket#close()
-        */
-       public static void close(ServerSocket serverSocket) {
-               if (serverSocket != null) {
-                       try {
-                               serverSocket.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given socket.
-        *
-        * @param socket
-        *            The socket to close
-        * @see Socket#close()
-        */
-       public static void close(Socket socket) {
-               if (socket != null) {
-                       try {
-                               socket.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given input stream.
-        *
-        * @param inputStream
-        *            The input stream to close
-        * @see InputStream#close()
-        */
-       public static void close(InputStream inputStream) {
-               if (inputStream != null) {
-                       try {
-                               inputStream.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given output stream.
-        *
-        * @param outputStream
-        *            The output stream to close
-        * @see OutputStream#close()
-        */
-       public static void close(OutputStream outputStream) {
-               if (outputStream != null) {
-                       try {
-                               outputStream.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given reader.
-        *
-        * @param reader
-        *            The reader to close
-        * @see Reader#close()
-        */
-       public static void close(Reader reader) {
-               if (reader != null) {
-                       try {
-                               reader.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-       /**
-        * Closes the given writer.
-        *
-        * @param writer
-        *            The write to close
-        * @see Writer#close()
-        */
-       public static void close(Writer writer) {
-               if (writer != null) {
-                       try {
-                               writer.close();
-                       } catch (IOException ioe1) {
-                       }
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/util/io/LineInputStream.java b/src/de/todesbaum/util/io/LineInputStream.java
deleted file mode 100644 (file)
index 4fe0d07..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * jSite - LineInputStream.java - Copyright © 2006–2012 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$
- */
-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/StreamCopier.java b/src/de/todesbaum/util/io/StreamCopier.java
deleted file mode 100644 (file)
index c7d99de..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * jSite - StreamCopier.java - Copyright © 2006–2012 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.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.EventListener;
-
-/**
- * Copies input from an {@link InputStream} to an {@link OutputStream}.
- *
- * @author <a href="mailto:droden@gmail.com">David Roden</a>
- * @version $Id$
- */
-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 the stream data. If the input stream is depleted before the
-        * requested number of bytes have been read an {@link EOFException} is
-        * thrown.
-        *
-        * @param progressListener
-        *            The progress listener (may be {@code null})
-        * @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(ProgressListener progressListener) throws EOFException, IOException {
-               copy(inputStream, outputStream, length, bufferSize, progressListener);
-       }
-
-       /**
-        * 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>.
-        *
-        * @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 progressListener
-        *            The progress listener (may be {@code null})
-        * @throws IOException
-        *             if an I/O exception occurs
-        */
-       public static void copy(InputStream inputStream, OutputStream outputStream, long length, ProgressListener progressListener) throws IOException {
-               copy(inputStream, outputStream, length, BUFFER_SIZE, progressListener);
-       }
-
-       /**
-        * 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 {
-               copy(inputStream, outputStream, length, bufferSize, null);
-       }
-
-       /**
-        * 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
-        * @param progressListener
-        *            The progress listener (may be {@code null})
-        * @throws IOException
-        *             if an I/O exception occurs
-        */
-       public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize, ProgressListener progressListener) 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;
-                       if (progressListener != null) {
-                               progressListener.onProgress(length - remaining, length);
-                       }
-               }
-       }
-
-       /**
-        * Interface for objects that want to be notified about the progress of a
-        * {@link StreamCopier#copy()} operation.
-        *
-        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
-        */
-       public static interface ProgressListener extends EventListener {
-
-               /**
-                * Notifiies a listener that a copy process made some progress.
-                *
-                * @param copied
-                *            The number of bytes that have already been copied
-                * @param length
-                *            The total number of bytes that will be copied
-                */
-               public void onProgress(long copied, long length);
-
-       }
-
-}
diff --git a/src/de/todesbaum/util/io/TeeOutputStream.java b/src/de/todesbaum/util/io/TeeOutputStream.java
deleted file mode 100644 (file)
index 78860dc..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * jSite - TeeOutputStream.java - Copyright © 2010 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 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.todesbaum.util.io;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * {@link OutputStream} that sends all data it receives to multiple other output
- * streams. If an error occurs during a {@link #write(int)} to one of the
- * underlying output streams no guarantees are made about how much data is sent
- * to each stream, i.e. there is no good way to recover from such an error.
- *
- * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
- */
-public class TeeOutputStream extends OutputStream {
-
-       /** The output streams. */
-       private final OutputStream[] outputStreams;
-
-       /**
-        * Creates a new tee output stream that sends all to all given output
-        * streams.
-        *
-        * @param outputStreams
-        *            The output streams to send all data to
-        */
-       public TeeOutputStream(OutputStream... outputStreams) {
-               this.outputStreams = outputStreams;
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * An effort is made to close all output streams. If multiple exceptions
-        * occur, only the first exception is thrown after all output streams have
-        * been tried to close.
-        */
-       @Override
-       public void close() throws IOException {
-               IOException occuredException = null;
-               for (OutputStream outputStream : outputStreams) {
-                       try {
-                               outputStream.flush();
-                       } catch (IOException ioe1) {
-                               if (occuredException == null) {
-                                       occuredException = ioe1;
-                               }
-                       }
-               }
-               if (occuredException != null) {
-                       throw occuredException;
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        * <p>
-        * An effort is made to flush all output streams. If multiple exceptions
-        * occur, only the first exception is thrown after all output streams have
-        * been tried to flush.
-        */
-       @Override
-       public void flush() throws IOException {
-               IOException occuredException = null;
-               for (OutputStream outputStream : outputStreams) {
-                       try {
-                               outputStream.flush();
-                       } catch (IOException ioe1) {
-                               if (occuredException == null) {
-                                       occuredException = ioe1;
-                               }
-                       }
-               }
-               if (occuredException != null) {
-                       throw occuredException;
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void write(byte[] buffer) throws IOException {
-               for (OutputStream outputStream : outputStreams) {
-                       outputStream.write(buffer);
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void write(byte[] buffer, int offset, int length) throws IOException {
-               for (OutputStream outputStream : outputStreams) {
-                       outputStream.write(buffer, offset, length);
-               }
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void write(int data) throws IOException {
-               for (OutputStream outputStream : outputStreams) {
-                       outputStream.write(data);
-               }
-       }
-
-}
diff --git a/src/de/todesbaum/util/io/TempFileInputStream.java b/src/de/todesbaum/util/io/TempFileInputStream.java
deleted file mode 100644 (file)
index 43e2047..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * jSite - TempFileInputStream.java - Copyright © 2006–2012 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);
-               tempFile = file;
-       }
-
-       @Override
-       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
deleted file mode 100644 (file)
index 9e380a5..0000000
+++ /dev/null
@@ -1,834 +0,0 @@
-/* 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
deleted file mode 100644 (file)
index 39a5ce2..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * jSite - SortedListModel.java - Copyright © 2006–2012 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;
-
-/**
- * @param <T>
- *            The type of the elements
- * @author David Roden &lt;droden@gmail.com&gt;
- */
-public class SortedListModel<T extends Comparable<T>> extends AbstractListModel implements List<T> {
-
-       /** The elements. */
-       private List<T> elements = new ArrayList<T>();
-
-       /**
-        * {@inheritDoc}
-        */
-       public int getSize() {
-               return size();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public Object getElementAt(int index) {
-               return elements.get(index);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public void add(int index, T element) {
-               elements.add(index, element);
-               Collections.sort(elements);
-               fireContentsChanged(this, 0, size());
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public boolean add(T o) {
-               boolean result = elements.add(o);
-               Collections.sort(elements);
-               fireContentsChanged(this, 0, size());
-               return result;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public boolean addAll(Collection<? extends T> c) {
-               boolean result = elements.addAll(c);
-               Collections.sort(elements);
-               fireContentsChanged(this, 0, size());
-               return result;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public boolean addAll(int index, Collection<? extends T> 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}
-        */
-       @Override
-       public boolean equals(Object o) {
-               return elements.equals(o);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public T get(int index) {
-               return elements.get(index);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       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<T> iterator() {
-               return elements.iterator();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public int lastIndexOf(Object o) {
-               return elements.lastIndexOf(o);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public ListIterator<T> listIterator() {
-               return elements.listIterator();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public ListIterator<T> listIterator(int index) {
-               return elements.listIterator(index);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public T 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 T set(int index, T element) {
-               T result = elements.set(index, element);
-               Collections.sort(elements);
-               fireContentsChanged(this, 0, size());
-               return result;
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public int size() {
-               return elements.size();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public List<T> subList(int fromIndex, int toIndex) {
-               return elements.subList(fromIndex, toIndex);
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public Object[] toArray() {
-               return elements.toArray();
-       }
-
-       /**
-        * {@inheritDoc}
-        */
-       public <U extends Object> U[] toArray(U[] a) {
-               return elements.toArray(a);
-       }
-
-}
diff --git a/src/de/todesbaum/util/swing/TLabel.java b/src/de/todesbaum/util/swing/TLabel.java
deleted file mode 100644 (file)
index 0393f5c..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * jSite - TLabel.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 464ac69..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * jSite - TWizard.java - Copyright © 2006–2012 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$
- */
-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;
-
-       @Override
-       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
deleted file mode 100644 (file)
index a10b475..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * jSite - TWizardPage.java - Copyright © 2006–2012 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$
- */
-public class TWizardPage extends JPanel {
-
-       protected final TWizard wizard;
-       protected String heading;
-       protected String description;
-
-       public TWizardPage(final TWizard wizard) {
-               this.wizard = wizard;
-       }
-
-       public TWizardPage(final TWizard wizard, String heading) {
-               this.wizard = wizard;
-               this.heading = heading;
-       }
-
-       public TWizardPage(final TWizard wizard, String heading, String description) {
-               this(wizard, 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
deleted file mode 100644 (file)
index 987075d..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * jSite - WizardListener.java - Copyright © 2006–2012 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$
- */
-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
deleted file mode 100644 (file)
index 8351581..0000000
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * jSite - SimpleXML.java - Copyright © 2006–2012 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.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;
-       }
-
-       /**
-        * Returns the value of this node. If the node does not have a value, the
-        * given default value is returned.
-        *
-        *@param defaultValue
-        *            The default value to return if the node does not have a value
-        * @return The value of this node
-        */
-       public String getValue(String defaultValue) {
-               return (value == null) ? defaultValue : 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"))) {
-                               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
deleted file mode 100644 (file)
index 8b6b259..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * jSite - XML.java - Copyright © 2006–2012 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.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;
-       }
-
-}
diff --git a/src/main/java/de/todesbaum/jsite/application/AbortedException.java b/src/main/java/de/todesbaum/jsite/application/AbortedException.java
new file mode 100644 (file)
index 0000000..0dfd8c7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * jSite - AbortedException.java - Copyright © 2010–2012 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;
+
+/**
+ * Marker exception that signals that the user aborted an insert.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class AbortedException extends Exception {
+
+       /* nothing here. */
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/FileOption.java b/src/main/java/de/todesbaum/jsite/application/FileOption.java
new file mode 100644 (file)
index 0000000..c8824de
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * jSite - FileOption.java - Copyright © 2006–2012 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;
+
+/**
+ * Container for various file options.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class FileOption {
+
+       /** The default for the insert state. */
+       private static final boolean DEFAULT_INSERT = true;
+
+       /** The default for the insert redirect state. */
+       private static final boolean DEFAULT_INSERT_REDIRECT = true;
+
+       /** The default for the custom key. */
+       private static final String DEFAULT_CUSTOM_KEY = "CHK@";
+
+       /** The default changed name. */
+       private static final String DEFAULT_CHANGED_NAME = null;
+
+       /** The insert state. */
+       private boolean insert;
+
+       /** Whether to force an insert. */
+       private boolean forceInsert;
+
+       /** Whether to insert a redirect. */
+       private boolean insertRedirect;
+
+       /** The hash of the last insert. */
+       private String lastInsertHash;
+
+       /** The edition of the last insert. */
+       private int lastInsertEdition;
+
+       /** The filename of the last insert. */
+       private String lastInsertFilename;
+
+       /** The current hash of the file. */
+       private String currentHash;
+
+       /** The custom key. */
+       private String customKey;
+
+       /** The changed name. */
+       private String changedName;
+
+       /** The default MIME type. */
+       private final String defaultMimeType;
+
+       /** The current MIME type. */
+       private String mimeType;
+
+       /**
+        * Creates new file options.
+        *
+        * @param defaultMimeType
+        *            The default MIME type of the file
+        */
+       public FileOption(String defaultMimeType) {
+               insert = DEFAULT_INSERT;
+               insertRedirect = DEFAULT_INSERT_REDIRECT;
+               customKey = DEFAULT_CUSTOM_KEY;
+               changedName = DEFAULT_CHANGED_NAME;
+               this.defaultMimeType = defaultMimeType;
+               mimeType = defaultMimeType;
+       }
+
+       /**
+        * Returns the custom key. The custom key is only used when
+        * {@link #isInsert()} and {@link #isInsertRedirect()} both return {@code
+        * true}.
+        *
+        * @return The custom key
+        */
+       public String getCustomKey() {
+               return customKey;
+       }
+
+       /**
+        * Sets the custom key. The custom key is only used when {@link #isInsert()}
+        * and {@link #isInsertRedirect()} both return {@code true}.
+        *
+        * @param customKey
+        *            The custom key
+        */
+       public void setCustomKey(String customKey) {
+               if (customKey == null) {
+                       this.customKey = "";
+               } else {
+                       this.customKey = customKey;
+               }
+       }
+
+       /**
+        * Returns whether the file should be inserted. If a file is not inserted
+        * and {@link #isInsertRedirect()} is also {@code false}, the file will not
+        * be inserted at all.
+        *
+        * @see #setCustomKey(String)
+        * @return <code>true</code> if the file should be inserted,
+        *         <code>false</code> otherwise
+        */
+       public boolean isInsert() {
+               return insert;
+       }
+
+       /**
+        * Sets whether the file should be inserted. If a file is not inserted and
+        * {@link #isInsertRedirect()} is also {@code false}, the file will not be
+        * inserted at all.
+        *
+        * @param insert
+        *            <code>true</code> if the file should be inserted,
+        *            <code>false</code> otherwise
+        */
+       public void setInsert(boolean insert) {
+               this.insert = insert;
+       }
+
+       /**
+        * Returns whether the insert of this file should be forced, even if its
+        * current hash matches the last insert hash.
+        *
+        * @return {@code true} to force the insert of this file, {@code false}
+        *         otherwise
+        */
+       public boolean isForceInsert() {
+               return forceInsert;
+       }
+
+       /**
+        * Sets whether to force the insert of this file, even if its current hash
+        * matches the last insert hash.
+        *
+        * @param forceInsert
+        *            {@code true} to force the insert of this file, {@code false}
+        *            otherwise
+        * @return These file options
+        */
+       public FileOption setForceInsert(boolean forceInsert) {
+               this.forceInsert = forceInsert;
+               return this;
+       }
+
+       /**
+        * Returns whether a redirect to a different key should be inserted. This
+        * will only matter if {@link #isInsert()} returns {@code false}. The key
+        * that should be redirected to still needs to be specified via
+        * {@link #setCustomKey(String)}.
+        *
+        * @return {@code true} if a redirect should be inserted, {@code false}
+        *         otherwise
+        */
+       public boolean isInsertRedirect() {
+               return insertRedirect;
+       }
+
+       /**
+        * Sets whether a redirect should be inserted. This will only matter if
+        * {@link #isInsert()} returns {@code false}, i.e. it has been
+        * {@link #setInsert(boolean)} to {@code false}. The key that should be
+        * redirected to still needs to be specified via
+        * {@link #setCustomKey(String)}.
+        *
+        * @param insertRedirect
+        *            {@code true} if a redirect should be inserted, {@code false}
+        *            otherwise
+        */
+       public void setInsertRedirect(boolean insertRedirect) {
+               this.insertRedirect = insertRedirect;
+       }
+
+       /**
+        * Returns the hash of the file when it was last inserted
+        *
+        * @return The last hash of the file
+        */
+       public String getLastInsertHash() {
+               return lastInsertHash;
+       }
+
+       /**
+        * Sets the hash of the file when it was last inserted.
+        *
+        * @param lastInsertHash
+        *            The last hash of the file
+        * @return These file options
+        */
+       public FileOption setLastInsertHash(String lastInsertHash) {
+               this.lastInsertHash = lastInsertHash;
+               return this;
+       }
+
+       /**
+        * Returns the last edition at which this file was inserted.
+        *
+        * @return The last insert edition of this file
+        */
+       public int getLastInsertEdition() {
+               return lastInsertEdition;
+       }
+
+       /**
+        * Sets the last insert edition of this file.
+        *
+        * @param lastInsertEdition
+        *            The last insert edition of this file
+        * @return These file options
+        */
+       public FileOption setLastInsertEdition(int lastInsertEdition) {
+               this.lastInsertEdition = lastInsertEdition;
+               return this;
+       }
+
+       /**
+        * Returns the name of the file when it was last inserted.
+        *
+        * @return The name of the file at the last insert
+        */
+       public String getLastInsertFilename() {
+               return lastInsertFilename;
+       }
+
+       /**
+        * Sets the name of the file when it was last inserted.
+        *
+        * @param lastInsertFilename
+        *            The name of the file at the last insert.
+        * @return These file options
+        */
+       public FileOption setLastInsertFilename(String lastInsertFilename) {
+               this.lastInsertFilename = lastInsertFilename;
+               return this;
+       }
+
+       /**
+        * Returns the current hash of the file. This value is ony a temporary value
+        * that is copied to {@link #getLastInsertHash()} when a project has
+        * finished inserting.
+        *
+        * @see Project#onSuccessfulInsert()
+        * @return The current hash of the file
+        */
+       public String getCurrentHash() {
+               return currentHash;
+       }
+
+       /**
+        * Sets the current hash of the file.
+        *
+        * @param currentHash
+        *            The current hash of the file
+        * @return These file options
+        */
+       public FileOption setCurrentHash(String currentHash) {
+               this.currentHash = currentHash;
+               return this;
+       }
+
+       /**
+        * Returns whether this file has a changed name. Use
+        * {@link #getChangedName()} is this method returns {@code true}.
+        *
+        * @return {@code true} if this file has a changed name, {@code false}
+        *         otherwise
+        */
+       public boolean hasChangedName() {
+               return (changedName != null) && (changedName.length() > 0);
+       }
+
+       /**
+        * Returns the changed name for this file. This method will return {@code
+        * null} or an empty {@link String} if this file should not be renamed.
+        *
+        * @return The changed name, or {@code null} if this file should not be
+        *         renamed
+        */
+       public String getChangedName() {
+               return changedName;
+       }
+
+       /**
+        * Sets the changed name for this file. Setting the changed file to {@code
+        * null} or an empty {@link String} will disable renaming.
+        *
+        * @param changedName
+        *            The new changed name for this file
+        */
+       public void setChangedName(String changedName) {
+               this.changedName = changedName;
+       }
+
+       /**
+        * Sets the MIME type of the file. Setting the MIME type to
+        * <code>null</code> will set the MIME type to the default MIME type.
+        *
+        * @param mimeType
+        *            The MIME type of the file
+        */
+       public void setMimeType(String mimeType) {
+               if (mimeType == null) {
+                       this.mimeType = defaultMimeType;
+               } else {
+                       this.mimeType = mimeType;
+               }
+       }
+
+       /**
+        * Returns the MIME type of the file. If no custom MIME type has been set,
+        * the default MIME type is returned.
+        *
+        * @return The MIME type of the file
+        */
+       public String getMimeType() {
+               return mimeType;
+       }
+
+       /**
+        * Returns whether the options for this file have been modified, i.e. are
+        * not at their default values.
+        *
+        * @return <code>true</code> if the options have been modified,
+        *         <code>false</code> if they are at default values
+        */
+       public boolean isCustom() {
+               if (insert != DEFAULT_INSERT) {
+                       return true;
+               }
+               if (!customKey.equals(DEFAULT_CUSTOM_KEY)) {
+                       return true;
+               }
+               if (((changedName != null) && !changedName.equals(DEFAULT_CHANGED_NAME)) || ((DEFAULT_CHANGED_NAME != null) && !DEFAULT_CHANGED_NAME.equals(changedName))) {
+                       return true;
+               }
+               if (!defaultMimeType.equals(mimeType)) {
+                       return true;
+               }
+               if (insertRedirect != DEFAULT_INSERT_REDIRECT) {
+                       return true;
+               }
+               return false;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java b/src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java
new file mode 100644 (file)
index 0000000..78250b2
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * jSite - Freenet7Interface.java - Copyright © 2006–2012 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;
+
+/**
+ * Interface for freenet-related operations.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Freenet7Interface {
+
+       /** Random number to differentiate several jSites. */
+       private static final int number = (int) (Math.random() * Integer.MAX_VALUE);
+
+       /** Counter. */
+       private static int counter = 0;
+
+       /** The node to connect to. */
+       private Node node;
+
+       /** The connection to the node. */
+       private Connection connection;
+
+       /**
+        * Sets the hostname of the node. The default port for FCP2 connections ({@link Node#DEFAULT_PORT})
+        * is used.
+        *
+        * @param hostname
+        *            The hostname of the node
+        */
+       public void setNodeAddress(String hostname) {
+               node = new Node(hostname);
+               connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
+       }
+
+       /**
+        * Sets the hostname and the port of the node.
+        *
+        * @param hostname
+        *            The hostname of the node
+        * @param port
+        *            The port number of the node
+        */
+       public void setNodeAddress(String hostname, int port) {
+               node = new Node(hostname, port);
+               connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
+       }
+
+       /**
+        * Sets hostname and port from the given node.
+        *
+        * @param node
+        *            The node to get the hostname and port from
+        */
+       public void setNode(de.todesbaum.jsite.application.Node node) {
+               if (node != null) {
+                       this.node = new Node(node.getHostname(), node.getPort());
+                       connection = new Connection(node, "jSite-" + number + "-connection-" + counter++);
+               } else {
+                       this.node = null;
+                       connection = null;
+               }
+       }
+
+       /**
+        * Removes the current node from the interface.
+        */
+       public void removeNode() {
+               node = null;
+               connection = null;
+       }
+
+       /**
+        * Returns the node this interface is connecting to.
+        *
+        * @return The node
+        */
+       public Node getNode() {
+               return node;
+       }
+
+       /**
+        * Creates a new connection to the current node with the given identifier.
+        *
+        * @param identifier
+        *            The identifier of the connection
+        * @return The connection to the node
+        */
+       public Connection getConnection(String identifier) {
+               return new Connection(node, identifier);
+       }
+
+       /**
+        * Checks whether the current node is connected. If the node is not
+        * connected, a connection will be tried.
+        *
+        * @return <code>true</code> if the node is connected, <code>false</code>
+        *         otherwise
+        * @throws IOException
+        *             if an I/O error occurs communicating with the node
+        */
+       public boolean isNodePresent() throws IOException {
+               if (!connection.isConnected()) {
+                       return connection.connect();
+               }
+               return true;
+       }
+
+       /**
+        * Generates an SSK key pair.
+        *
+        * @return An array of strings, the first one being the generated private
+        *         (insert) URI and the second one being the generated public
+        *         (request) URI
+        * @throws IOException
+        *             if an I/O error occurs communicating with the node
+        */
+       public String[] generateKeyPair() throws IOException {
+               if (!isNodePresent()) {
+                       throw new IOException("Node is offline.");
+               }
+               GenerateSSK generateSSK = new GenerateSSK();
+               Client client = new Client(connection, generateSSK);
+               Message keypairMessage = client.readMessage();
+               return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") };
+       }
+
+       /**
+        * Checks whether the interface has already been configured with a node.
+        *
+        * @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/main/java/de/todesbaum/jsite/application/InsertListener.java b/src/main/java/de/todesbaum/jsite/application/InsertListener.java
new file mode 100644 (file)
index 0000000..9b6b332
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * jSite - InsertListener.java - Copyright © 2006–2012 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;
+
+/**
+ * Interface for objects that want to be notified abount insert events.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public interface InsertListener extends EventListener {
+
+       /**
+        * Enumeration for the different error situations.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       public static enum ErrorType {
+
+               /** The key does already exist. */
+               KEY_COLLISION,
+
+               /** The route to the key was not found. */
+               ROUTE_NOT_FOUND,
+
+               /** The data was not found. */
+               DATA_NOT_FOUND,
+
+               /** Error in the FCP communication. */
+               FCP_ERROR,
+
+               /** General error in the communication. */
+               IO_ERROR
+       }
+
+       /**
+        * Notifies a listener that an insert has started.
+        *
+        * @param project
+        *            The project that is now being inserted
+        */
+       public void projectInsertStarted(Project project);
+
+       /**
+        * Notifies a listener that the upload of a project has finished and the
+        * inserting will start now.
+        *
+        * @param project
+        *            The project that has been uploaded
+        */
+       public void projectUploadFinished(Project project);
+
+       /**
+        * Notifies a listener that a project insert has generated a URI.
+        *
+        * @param project
+        *            The project being inserted
+        * @param uri
+        *            The generated URI
+        */
+       public void projectURIGenerated(Project project, String uri);
+
+       /**
+        * Notifies a listener that an insert has made some progress.
+        *
+        * @param project
+        *            The project being inserted
+        * @param succeeded
+        *            The number of succeeded blocks
+        * @param failed
+        *            The number of failed blocks
+        * @param fatal
+        *            The number of fatally failed blocks
+        * @param total
+        *            The total number of blocks
+        * @param finalized
+        *            <code>true</code> if the total number of blocks has been
+        *            finalized, <code>false</code> otherwise
+        */
+       public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized);
+
+       /**
+        * Notifies a listener that a project insert has finished.
+        *
+        * @param project
+        *            The project being inserted
+        * @param success
+        *            <code>true</code> if the insert succeeded, <code>false</code>
+        *            otherwise
+        * @param cause
+        *            The cause of a failure, if any (may be <code>null</code>)
+        */
+       public void projectInsertFinished(Project project, boolean success, Throwable cause);
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/KeyDialog.java b/src/main/java/de/todesbaum/jsite/application/KeyDialog.java
new file mode 100644 (file)
index 0000000..4b64f72
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * jSite - KeyDialog.java - Copyright © 2010–2012 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.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.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.SwingConstants;
+
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.jsite.i18n.I18nContainer;
+
+/**
+ * A dialog that lets the user edit the private and public key for a project.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class KeyDialog extends JDialog {
+
+       /** Interface to the freenet node. */
+       private final Freenet7Interface freenetInterface;
+
+       /** The public key. */
+       private String publicKey;
+
+       /** The private key. */
+       private String privateKey;
+
+       /** The “OK” button’s action. */
+       private Action okAction;
+
+       /** The “Cancel” button’s action. */
+       private Action cancelAction;
+
+       /** The “Regenerate” button’s action. */
+       private Action generateAction;
+
+       /** The text field for the private key. */
+       private JTextField privateKeyTextField;
+
+       /** The text field for the public key. */
+       private JTextField publicKeyTextField;
+
+       /** Whether the dialog was cancelled. */
+       private boolean cancelled;
+
+       /**
+        * Creates a new key dialog.
+        *
+        * @param freenetInterface
+        *            Interface to the freenet node
+        * @param parent
+        *            The parent frame
+        */
+       public KeyDialog(Freenet7Interface freenetInterface, JFrame parent) {
+               super(parent, I18n.getMessage("jsite.key-dialog.title"), true);
+               this.freenetInterface = freenetInterface;
+               addWindowListener(new WindowAdapter() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void windowClosing(WindowEvent windowEvent) {
+                               actionCancel();
+                       }
+               });
+               initDialog();
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns whether the dialog was cancelled.
+        *
+        * @return {@code true} if the dialog was cancelled, {@code false} otherwise
+        */
+       public boolean wasCancelled() {
+               return cancelled;
+       }
+
+       /**
+        * Returns the public key.
+        *
+        * @return The public key
+        */
+       public String getPublicKey() {
+               return publicKey;
+       }
+
+       /**
+        * Sets the public key.
+        *
+        * @param publicKey
+        *            The public key
+        */
+       public void setPublicKey(String publicKey) {
+               this.publicKey = publicKey;
+               publicKeyTextField.setText(publicKey);
+               pack();
+       }
+
+       /**
+        * Returns the private key.
+        *
+        * @return The private key
+        */
+       public String getPrivateKey() {
+               return privateKey;
+       }
+
+       /**
+        * Sets the private key.
+        *
+        * @param privateKey
+        *            The private key
+        */
+       public void setPrivateKey(String privateKey) {
+               this.privateKey = privateKey;
+               privateKeyTextField.setText(privateKey);
+               pack();
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pack() {
+               super.pack();
+               Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+               setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2);
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Creates all necessary actions.
+        */
+       private void createActions() {
+               okAction = new AbstractAction(I18n.getMessage("jsite.general.ok")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionOk();
+                       }
+               };
+               okAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.ok.tooltip"));
+               okAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ENTER);
+
+               cancelAction = new AbstractAction(I18n.getMessage("jsite.general.cancel")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionCancel();
+                       }
+               };
+               cancelAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.cancel.tooltip"));
+               cancelAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ESCAPE);
+
+               generateAction = new AbstractAction(I18n.getMessage("jsite.key-dialog.button.generate")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionGenerate();
+                       }
+               };
+               generateAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.generate.tooltip"));
+               generateAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK));
+       }
+
+       /**
+        * Initializes the dialog and all its components.
+        */
+       private void initDialog() {
+               createActions();
+               JPanel dialogPanel = new JPanel(new BorderLayout(12, 12));
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
+
+               JPanel contentPanel = new JPanel(new GridBagLayout());
+               dialogPanel.add(contentPanel, BorderLayout.CENTER);
+
+               final JLabel keysLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.keys"));
+               contentPanel.add(keysLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
+
+               final JLabel privateKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.private-key"));
+               contentPanel.add(privateKeyLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
+
+               privateKeyTextField = new JTextField();
+               contentPanel.add(privateKeyTextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0));
+
+               final JLabel publicKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.public-key"));
+               contentPanel.add(publicKeyLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               publicKeyTextField = new JTextField();
+               contentPanel.add(publicKeyTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0));
+
+               final JLabel actionsLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.actions"));
+               contentPanel.add(actionsLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
+
+               JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12));
+               actionButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+               contentPanel.add(actionButtonPanel, new GridBagConstraints(0, 4, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
+
+               actionButtonPanel.add(new JButton(generateAction));
+
+               JPanel separatorPanel = new JPanel(new BorderLayout(12, 12));
+               dialogPanel.add(separatorPanel, BorderLayout.PAGE_END);
+               separatorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.PAGE_START);
+
+               JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
+               buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+               separatorPanel.add(buttonPanel, BorderLayout.CENTER);
+               buttonPanel.add(new JButton(okAction));
+               buttonPanel.add(new JButton(cancelAction));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               keysLabel.setText(I18n.getMessage("jsite.key-dialog.label.keys"));
+                               privateKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.private-key"));
+                               publicKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.public-key"));
+                               actionsLabel.setText(I18n.getMessage("jsite.key-dialog.label.actions"));
+                       }
+               });
+
+               getContentPane().add(dialogPanel, BorderLayout.CENTER);
+               pack();
+               setResizable(false);
+       }
+
+       //
+       // PRIVATE ACTIONS
+       //
+
+       /**
+        * Quits the dialog, accepting all changes.
+        */
+       private void actionOk() {
+               publicKey = publicKeyTextField.getText();
+               privateKey = privateKeyTextField.getText();
+               cancelled = false;
+               setVisible(false);
+       }
+
+       /**
+        * Quits the dialog, discarding all changes.
+        */
+       private void actionCancel() {
+               cancelled = true;
+               setVisible(false);
+       }
+
+       /**
+        * Generates a new key pair.
+        */
+       private void actionGenerate() {
+               if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.generate-new-key"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
+                       return;
+               }
+               String[] keyPair = null;
+               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;
+               }
+               publicKeyTextField.setText(keyPair[1].substring(keyPair[1].indexOf('@') + 1, keyPair[1].lastIndexOf('/')));
+               privateKeyTextField.setText(keyPair[0].substring(keyPair[0].indexOf('@') + 1, keyPair[0].lastIndexOf('/')));
+               pack();
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/Node.java b/src/main/java/de/todesbaum/jsite/application/Node.java
new file mode 100644 (file)
index 0000000..df7492f
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * jSite - Node.java - Copyright © 2006–2012 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;
+
+/**
+ * Container for node information.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Node extends de.todesbaum.util.freenet.fcp2.Node {
+
+       /** The name of the node. */
+       protected String name;
+
+       /**
+        * Creates a new node with the given hostname and the default port.
+        *
+        * @see de.todesbaum.util.freenet.fcp2.Node#DEFAULT_PORT
+        * @param hostname
+        *            The hostname of the new node
+        */
+       public Node(String hostname) {
+               this(hostname, DEFAULT_PORT);
+       }
+
+       /**
+        * Creates a new node with the given hostname and port.
+        *
+        * @param hostname
+        *            The hostname of the new node
+        * @param port
+        *            The port of the new node
+        */
+       public Node(String hostname, int port) {
+               this(hostname, port, "");
+       }
+
+       /**
+        * Creates a new node with the given hostname, port, and name.
+        *
+        * @param hostname
+        *            The hostname of the new node
+        * @param port
+        *            The port of the new node
+        * @param name
+        *            The name of the node
+        */
+       public Node(String hostname, int port, String name) {
+               super(hostname, port);
+               this.name = name;
+       }
+
+       /**
+        * Creates a new node that gets its settings from the given node.
+        *
+        * @param node
+        *            The node to copy
+        */
+       public Node(Node node) {
+               this(node.getHostname(), node.getPort());
+       }
+
+       /**
+        * Creates a new node from the given node, overwriting the name.
+        *
+        * @param node
+        *            The node to copy from
+        * @param name
+        *            The new name of the node
+        */
+       public Node(Node node, String name) {
+               this(node.getHostname(), node.getPort(), name);
+       }
+
+       /**
+        * Sets the name of the node.
+        *
+        * @param name
+        *            The name of the node
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       /**
+        * Returns the name of the node.
+        *
+        * @return The name of the node
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Sets the hostname of the node.
+        *
+        * @param hostname
+        *            The hostname of the node
+        */
+       public void setHostname(String hostname) {
+               this.hostname = hostname;
+       }
+
+       /**
+        * Sets the port of the node.
+        *
+        * @param port
+        *            The port of the node
+        */
+       public void setPort(int port) {
+               this.port = port;
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * A node is considered as being equal to this node its name, hostname, and
+        * port equal their counterparts in this node.
+        */
+       @Override
+       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);
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * The hashcode for a node is created from its name, its hostname, and its
+        * port.
+        */
+       @Override
+       public int hashCode() {
+               return name.hashCode() ^ hostname.hashCode() ^ port;
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * Creates a textual representation of this node.
+        */
+       @Override
+       public String toString() {
+               return name + " (" + hostname + ":" + port + ")";
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/Project.java b/src/main/java/de/todesbaum/jsite/application/Project.java
new file mode 100644 (file)
index 0000000..fa0b774
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * jSite - Project.java - Copyright © 2006–2012 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 java.util.Map.Entry;
+
+import de.todesbaum.util.mime.DefaultMIMETypes;
+
+/**
+ * Container for project information.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Project implements Comparable<Project> {
+
+       /** The name of the project. */
+       protected String name;
+
+       /** The description of the project. */
+       protected String description;
+
+       /** The insert URI of the project. */
+       protected String insertURI;
+
+       /** The request URI of the project. */
+       protected String requestURI;
+
+       /** The index file of the project. */
+       protected String indexFile;
+
+       /** The local path of the project. */
+       protected String localPath;
+
+       /** The remote path of the URI. */
+       protected String path;
+
+       /** The time of the last insertion. */
+       protected long lastInsertionTime;
+
+       /** The edition to insert to. */
+       protected int edition;
+
+       /** Whether to ignore hidden directory. */
+       private boolean ignoreHiddenFiles;
+
+       /** Options for files. */
+       protected Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
+
+       /**
+        * Empty constructor.
+        */
+       public Project() {
+               /* do nothing. */
+       }
+
+       /**
+        * Creates a new project from an existing one.
+        *
+        * @param project
+        *            The project to clone
+        */
+       public Project(Project project) {
+               name = project.name;
+               description = project.description;
+               insertURI = project.insertURI;
+               requestURI = project.requestURI;
+               path = project.path;
+               edition = project.edition;
+               localPath = project.localPath;
+               indexFile = project.indexFile;
+               lastInsertionTime = project.lastInsertionTime;
+               ignoreHiddenFiles = project.ignoreHiddenFiles;
+               fileOptions = new HashMap<String, FileOption>(project.fileOptions);
+       }
+
+       /**
+        * Returns the name of the project.
+        *
+        * @return The name of the project
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Sets the name of the project.
+        *
+        * @param name
+        *            The name of the project
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
+       /**
+        * Returns the description of the project.
+        *
+        * @return The description of the project
+        */
+       public String getDescription() {
+               return description;
+       }
+
+       /**
+        * Sets the description of the project.
+        *
+        * @param description
+        *            The description of the project
+        */
+       public void setDescription(String description) {
+               this.description = description;
+       }
+
+       /**
+        * Returns the local path of the project.
+        *
+        * @return The local path of the project
+        */
+       public String getLocalPath() {
+               return localPath;
+       }
+
+       /**
+        * Sets the local path of the project.
+        *
+        * @param localPath
+        *            The local path of the project
+        */
+       public void setLocalPath(String localPath) {
+               this.localPath = localPath;
+       }
+
+       /**
+        * Returns the name of the index file of the project, relative to the
+        * project’s local path.
+        *
+        * @return The name of the index file of the project
+        */
+       public String getIndexFile() {
+               return indexFile;
+       }
+
+       /**
+        * Sets the name of the index file of the project, relative to the project’s
+        * local path.
+        *
+        * @param indexFile
+        *            The name of the index file of the project
+        */
+       public void setIndexFile(String indexFile) {
+               this.indexFile = indexFile;
+       }
+
+       /**
+        * Returns the time the project was last inserted, in milliseconds since the
+        * epoch.
+        *
+        * @return The time of the last insertion
+        */
+       public long getLastInsertionTime() {
+               return lastInsertionTime;
+       }
+
+       /**
+        * Sets the time the project was last inserted, in milliseconds since the
+        * last epoch.
+        *
+        * @param lastInserted
+        *            The time of the last insertion
+        */
+       public void setLastInsertionTime(long lastInserted) {
+               lastInsertionTime = lastInserted;
+       }
+
+       /**
+        * Returns the remote path of the project. The remote path is the path that
+        * directly follows the request URI of the project.
+        *
+        * @return The remote path of the project
+        */
+       public String getPath() {
+               return path;
+       }
+
+       /**
+        * Sets the remote path of the project. The remote path is the path that
+        * directly follows the request URI of the project.
+        *
+        * @param path
+        *            The remote path of the project
+        */
+       public void setPath(String path) {
+               this.path = path;
+       }
+
+       /**
+        * Returns the insert URI of the project.
+        *
+        * @return The insert URI of the project
+        */
+       public String getInsertURI() {
+               return insertURI;
+       }
+
+       /**
+        * Sets the insert URI of the project.
+        *
+        * @param insertURI
+        *            The insert URI of the project
+        */
+       public void setInsertURI(String insertURI) {
+               this.insertURI = shortenURI(insertURI);
+       }
+
+       /**
+        * Returns the request URI of the project.
+        *
+        * @return The request URI of the project
+        */
+       public String getRequestURI() {
+               return requestURI;
+       }
+
+       /**
+        * Sets the request URI of the project.
+        *
+        * @param requestURI
+        *            The request URI of the project
+        */
+       public void setRequestURI(String requestURI) {
+               this.requestURI = shortenURI(requestURI);
+       }
+
+       /**
+        * Returns whether hidden files are ignored, i.e. not inserted.
+        *
+        * @return {@code true} if hidden files are not inserted, {@code false}
+        *         otherwise
+        */
+       public boolean isIgnoreHiddenFiles() {
+               return ignoreHiddenFiles;
+       }
+
+       /**
+        * Sets whether hidden files are ignored, i.e. not inserted.
+        *
+        * @param ignoreHiddenFiles
+        *            {@code true} if hidden files are not inserted, {@code false}
+        *            otherwise
+        */
+       public void setIgnoreHiddenFiles(boolean ignoreHiddenFiles) {
+               this.ignoreHiddenFiles = ignoreHiddenFiles;
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * This method returns the name of the project.
+        */
+       @Override
+       public String toString() {
+               return name;
+       }
+
+       /**
+        * Shortens the given URI by removing scheme and key-type prefixes.
+        *
+        * @param uri
+        *            The URI to shorten
+        * @return The shortened URI
+        */
+       private String shortenURI(String uri) {
+               String shortUri = uri;
+               if (shortUri.startsWith("freenet:")) {
+                       shortUri = shortUri.substring("freenet:".length());
+               }
+               if (shortUri.startsWith("SSK@")) {
+                       shortUri = shortUri.substring("SSK@".length());
+               }
+               if (shortUri.startsWith("USK@")) {
+                       shortUri = shortUri.substring("USK@".length());
+               }
+               if (shortUri.endsWith("/")) {
+                       shortUri = shortUri.substring(0, shortUri.length() - 1);
+               }
+               return shortUri;
+       }
+
+       /**
+        * Shortens the name of the given file by removing the local path of the
+        * project and leading file separators.
+        *
+        * @param file
+        *            The file whose name should be shortened
+        * @return The shortened name of the file
+        */
+       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;
+       }
+
+       /**
+        * Returns the options for the file with the given name. If the file does
+        * not yet have any options, a new set of default options is created and
+        * returned.
+        *
+        * @param filename
+        *            The name of the file, relative to the project root
+        * @return The options for the file
+        */
+       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;
+       }
+
+       /**
+        * Sets options for a file.
+        *
+        * @param filename
+        *            The filename to set the options for, relative to the project
+        *            root
+        * @param fileOption
+        *            The options to set for the file, or <code>null</code> to
+        *            remove the options for the file
+        */
+       public void setFileOption(String filename, FileOption fileOption) {
+               if (fileOption != null) {
+                       fileOptions.put(filename, fileOption);
+               } else {
+                       fileOptions.remove(filename);
+               }
+       }
+
+       /**
+        * Returns all file options.
+        *
+        * @return All file options
+        */
+       public Map<String, FileOption> getFileOptions() {
+               return Collections.unmodifiableMap(fileOptions);
+       }
+
+       /**
+        * Sets all file options.
+        *
+        * @param fileOptions
+        *            The file options
+        */
+       public void setFileOptions(Map<String, FileOption> fileOptions) {
+               this.fileOptions.clear();
+               this.fileOptions.putAll(fileOptions);
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * Projects are compared by their name only.
+        */
+       public int compareTo(Project project) {
+               return name.compareToIgnoreCase(project.name);
+       }
+
+       /**
+        * 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.
+        *
+        * @param offset
+        *            The offset for the edition number
+        * @return The final request URI
+        */
+       public String getFinalRequestURI(int offset) {
+               return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/";
+       }
+
+       /**
+        * Performs some post-processing on the project after it was inserted
+        * successfully. At the moment it copies the current hashes of all file
+        * options to the last insert hashes, updating the hashes for the next
+        * insert.
+        */
+       public void onSuccessfulInsert() {
+               for (Entry<String, FileOption> fileOptionEntry : fileOptions.entrySet()) {
+                       FileOption fileOption = fileOptionEntry.getValue();
+                       if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) {
+                               fileOption.setLastInsertEdition(edition);
+                               fileOption.setLastInsertHash(fileOption.getCurrentHash());
+                               fileOption.setLastInsertFilename(fileOption.hasChangedName() ? fileOption.getChangedName() : fileOptionEntry.getKey());
+                       }
+                       fileOption.setForceInsert(false);
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/ProjectInserter.java b/src/main/java/de/todesbaum/jsite/application/ProjectInserter.java
new file mode 100644 (file)
index 0000000..b5f2e16
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * jSite - ProjectInserter.java - Copyright © 2006–2012 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.todesbaum.jsite.gui.FileScanner;
+import de.todesbaum.jsite.gui.FileScanner.ScannedFile;
+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.ClientPutDir.ManifestPutter;
+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.PriorityClass;
+import de.todesbaum.util.freenet.fcp2.RedirectFileEntry;
+import de.todesbaum.util.freenet.fcp2.Verbosity;
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
+
+/**
+ * Manages project inserts.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ProjectInserter implements FileScannerListener, Runnable {
+
+       /** The logger. */
+       private static final Logger logger = Logger.getLogger(ProjectInserter.class.getName());
+
+       /** Random number for FCP instances. */
+       private static final int random = (int) (Math.random() * Integer.MAX_VALUE);
+
+       /** Counter for FCP connection identifier. */
+       private static int counter = 0;
+
+       /** The list of insert listeners. */
+       private List<InsertListener> insertListeners = new ArrayList<InsertListener>();
+
+       /** The freenet interface. */
+       protected Freenet7Interface freenetInterface;
+
+       /** The project to insert. */
+       protected Project project;
+
+       /** The file scanner. */
+       private FileScanner fileScanner;
+
+       /** Object used for synchronization. */
+       protected final Object lockObject = new Object();
+
+       /** The temp directory. */
+       private String tempDirectory;
+
+       /** The current connection. */
+       private Connection connection;
+
+       /** Whether the insert is cancelled. */
+       private volatile boolean cancelled = false;
+
+       /** Progress listener for payload transfers. */
+       private ProgressListener progressListener;
+
+       /** Whether to use “early encode.” */
+       private boolean useEarlyEncode;
+
+       /** The insert priority. */
+       private PriorityClass priority;
+
+       /** The manifest putter. */
+       private ManifestPutter manifestPutter;
+
+       /**
+        * Adds a listener to the list of registered listeners.
+        *
+        * @param insertListener
+        *            The listener to add
+        */
+       public void addInsertListener(InsertListener insertListener) {
+               insertListeners.add(insertListener);
+       }
+
+       /**
+        * Removes a listener from the list of registered listeners.
+        *
+        * @param insertListener
+        *            The listener to remove
+        */
+       public void removeInsertListener(InsertListener insertListener) {
+               insertListeners.remove(insertListener);
+       }
+
+       /**
+        * Notifies all listeners that the project insert has started.
+        *
+        * @see InsertListener#projectInsertStarted(Project)
+        */
+       protected void fireProjectInsertStarted() {
+               for (InsertListener insertListener : insertListeners) {
+                       insertListener.projectInsertStarted(project);
+               }
+       }
+
+       /**
+        * Notifies all listeners that the insert has generated a URI.
+        *
+        * @see InsertListener#projectURIGenerated(Project, String)
+        * @param uri
+        *            The generated URI
+        */
+       protected void fireProjectURIGenerated(String uri) {
+               for (InsertListener insertListener : insertListeners) {
+                       insertListener.projectURIGenerated(project, uri);
+               }
+       }
+
+       /**
+        * Notifies all listeners that the insert has made some progress.
+        *
+        * @see InsertListener#projectUploadFinished(Project)
+        */
+       protected void fireProjectUploadFinished() {
+               for (InsertListener insertListener : insertListeners) {
+                       insertListener.projectUploadFinished(project);
+               }
+       }
+
+       /**
+        * Notifies all listeners that the insert has made some progress.
+        *
+        * @see InsertListener#projectInsertProgress(Project, int, int, int, int,
+        *      boolean)
+        * @param succeeded
+        *            The number of succeeded blocks
+        * @param failed
+        *            The number of failed blocks
+        * @param fatal
+        *            The number of fatally failed blocks
+        * @param total
+        *            The total number of blocks
+        * @param finalized
+        *            <code>true</code> if the total number of blocks has already
+        *            been finalized, <code>false</code> otherwise
+        */
+       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);
+               }
+       }
+
+       /**
+        * Notifies all listeners the project insert has finished.
+        *
+        * @see InsertListener#projectInsertFinished(Project, boolean, Throwable)
+        * @param success
+        *            <code>true</code> if the project was inserted successfully,
+        *            <code>false</code> if it failed
+        * @param cause
+        *            The cause of the failure, if any
+        */
+       protected void fireProjectInsertFinished(boolean success, Throwable cause) {
+               for (InsertListener insertListener : insertListeners) {
+                       insertListener.projectInsertFinished(project, success, cause);
+               }
+       }
+
+       /**
+        * Sets the project to insert.
+        *
+        * @param project
+        *            The project to insert
+        */
+       public void setProject(Project project) {
+               this.project = project;
+       }
+
+       /**
+        * Sets the freenet interface to use.
+        *
+        * @param freenetInterface
+        *            The freenet interface to use
+        */
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       /**
+        * Sets the temp directory to use.
+        *
+        * @param tempDirectory
+        *            The temp directory to use, or {@code null} to use the system
+        *            default
+        */
+       public void setTempDirectory(String tempDirectory) {
+               this.tempDirectory = tempDirectory;
+       }
+
+       /**
+        * Sets whether to use the “early encode“ flag for the insert.
+        *
+        * @param useEarlyEncode
+        *            {@code true} to set the “early encode” flag for the insert,
+        *            {@code false} otherwise
+        */
+       public void setUseEarlyEncode(boolean useEarlyEncode) {
+               this.useEarlyEncode = useEarlyEncode;
+       }
+
+       /**
+        * Sets the insert priority.
+        *
+        * @param priority
+        *            The insert priority
+        */
+       public void setPriority(PriorityClass priority) {
+               this.priority = priority;
+       }
+
+       /**
+        * Sets the manifest putter to use for inserts.
+        *
+        * @param manifestPutter
+        *            The manifest putter to use
+        */
+       public void setManifestPutter(ManifestPutter manifestPutter) {
+               this.manifestPutter = manifestPutter;
+       }
+
+       /**
+        * Starts the insert.
+        *
+        * @param progressListener
+        *            Listener to notify on progress events
+        */
+       public void start(ProgressListener progressListener) {
+               cancelled = false;
+               this.progressListener = progressListener;
+               fileScanner = new FileScanner(project);
+               fileScanner.addFileScannerListener(this);
+               new Thread(fileScanner).start();
+       }
+
+       /**
+        * Stops the current insert.
+        */
+       public void stop() {
+               cancelled = true;
+               synchronized (lockObject) {
+                       if (connection != null) {
+                               connection.disconnect();
+                       }
+               }
+       }
+
+       /**
+        * Creates an input stream that delivers the given file, replacing edition
+        * tokens in the file’s content, if necessary.
+        *
+        * @param filename
+        *            The name of the file
+        * @param fileOption
+        *            The file options
+        * @param edition
+        *            The current edition
+        * @param length
+        *            An array containing a single long which is used to
+        *            <em>return</em> the final length of the file, after all
+        *            replacements
+        * @return The input stream for the file
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException {
+               File file = new File(project.getLocalPath(), filename);
+               length[0] = file.length();
+               return new FileInputStream(file);
+       }
+
+       /**
+        * Creates a file entry suitable for handing in to
+        * {@link ClientPutComplexDir#addFileEntry(FileEntry)}.
+        *
+        * @param file
+        *            The name and hash of the file to insert
+        * @param edition
+        *            The current edition
+        * @return A file entry for the given file
+        */
+       private FileEntry createFileEntry(ScannedFile file, int edition) {
+               FileEntry fileEntry = null;
+               String filename = file.getFilename();
+               FileOption fileOption = project.getFileOption(filename);
+               if (fileOption.isInsert()) {
+                       fileOption.setCurrentHash(file.getHash());
+                       /* check if file was modified. */
+                       if (!fileOption.isForceInsert() && file.getHash().equals(fileOption.getLastInsertHash())) {
+                               /* only insert a redirect. */
+                               logger.log(Level.FINE, String.format("Inserting redirect to edition %d for %s.", fileOption.getLastInsertEdition(), filename));
+                               return new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename());
+                       }
+                       try {
+                               long[] fileLength = new long[1];
+                               InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength);
+                               fileEntry = new DirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileEntryInputStream, fileLength[0]);
+                       } catch (IOException ioe1) {
+                               /* ignore, null is returned. */
+                       }
+               } else {
+                       if (fileOption.isInsertRedirect()) {
+                               fileEntry = new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileOption.getCustomKey());
+                       }
+               }
+               return fileEntry;
+       }
+
+       /**
+        * Validates the given project. The project will be checked for any invalid
+        * conditions, such as invalid insert or request keys, missing path names,
+        * missing default file, and so on.
+        *
+        * @param project
+        *            The project to check
+        * @return The encountered warnings and errors
+        */
+       public static CheckReport validateProject(Project project) {
+               CheckReport checkReport = new CheckReport();
+               if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) {
+                       checkReport.addIssue("error.no-local-path", true);
+               }
+               if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
+                       checkReport.addIssue("error.no-path", true);
+               }
+               if ((project.getIndexFile() == null) || (project.getIndexFile().length() == 0)) {
+                       checkReport.addIssue("warning.empty-index", false);
+               } else {
+                       File indexFile = new File(project.getLocalPath(), project.getIndexFile());
+                       if (!indexFile.exists()) {
+                               checkReport.addIssue("error.index-missing", true);
+                       }
+               }
+               String indexFile = project.getIndexFile();
+               boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0);
+               List<String> allowedIndexContentTypes = Arrays.asList("text/html", "application/xhtml+xml");
+               if (hasIndexFile && !allowedIndexContentTypes.contains(project.getFileOption(indexFile).getMimeType())) {
+                       checkReport.addIssue("warning.index-not-html", false);
+               }
+               Map<String, FileOption> fileOptions = project.getFileOptions();
+               Set<Entry<String, FileOption>> fileOptionEntries = fileOptions.entrySet();
+               boolean insert = fileOptionEntries.isEmpty();
+               for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
+                       String fileName = fileOptionEntry.getKey();
+                       FileOption fileOption = fileOptionEntry.getValue();
+                       insert |= fileOption.isInsert() || fileOption.isInsertRedirect();
+                       if (fileName.equals(project.getIndexFile()) && !fileOption.isInsert() && !fileOption.isInsertRedirect()) {
+                               checkReport.addIssue("error.index-not-inserted", true);
+                       }
+                       if (!fileOption.isInsert() && fileOption.isInsertRedirect() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(fileOption.getCustomKey()))) {
+                               checkReport.addIssue("error.no-custom-key", true, fileName);
+                       }
+               }
+               if (!insert) {
+                       checkReport.addIssue("error.no-files-to-insert", true);
+               }
+               Set<String> fileNames = new HashSet<String>();
+               for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
+                       FileOption fileOption = fileOptionEntry.getValue();
+                       if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) {
+                               logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey());
+                               continue;
+                       }
+                       String fileName = fileOptionEntry.getKey();
+                       if (fileOption.hasChangedName()) {
+                               fileName = fileOption.getChangedName();
+                       }
+                       logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() });
+                       if (!fileNames.add(fileName)) {
+                               checkReport.addIssue("error.duplicate-file", true, fileName);
+                       }
+               }
+               long totalSize = 0;
+               FileScanner fileScanner = new FileScanner(project);
+               final CountDownLatch completionLatch = new CountDownLatch(1);
+               fileScanner.addFileScannerListener(new FileScannerListener() {
+
+                       @Override
+                       public void fileScannerFinished(FileScanner fileScanner) {
+                               completionLatch.countDown();
+                       }
+               });
+               new Thread(fileScanner).start();
+               while (completionLatch.getCount() > 0) {
+                       try {
+                               completionLatch.await();
+                       } catch (InterruptedException ie1) {
+                               /* TODO: logging */
+                       }
+               }
+               for (ScannedFile scannedFile : fileScanner.getFiles()) {
+                       String fileName = scannedFile.getFilename();
+                       FileOption fileOption = project.getFileOption(fileName);
+                       if ((fileOption != null) && !fileOption.isInsert()) {
+                               continue;
+                       }
+                       totalSize += new File(project.getLocalPath(), fileName).length();
+               }
+               if (totalSize > 2 * 1024 * 1024) {
+                       checkReport.addIssue("warning.site-larger-than-2-mib", false);
+               }
+               return checkReport;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void run() {
+               fireProjectInsertStarted();
+               List<ScannedFile> files = fileScanner.getFiles();
+
+               /* create connection to node */
+               synchronized (lockObject) {
+                       connection = freenetInterface.getConnection("project-insert-" + random + counter++);
+               }
+               connection.setTempDirectory(tempDirectory);
+               boolean connected = false;
+               Throwable cause = null;
+               try {
+                       connected = connection.connect();
+               } catch (IOException e1) {
+                       cause = e1;
+               }
+
+               if (!connected || cancelled) {
+                       fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause);
+                       return;
+               }
+
+               Client client = new Client(connection);
+
+               /* collect files */
+               int edition = project.getEdition();
+               String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/";
+               ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI, tempDirectory);
+               if ((project.getIndexFile() != null) && (project.getIndexFile().length() > 0)) {
+                       putDir.setDefaultName(project.getIndexFile());
+               }
+               putDir.setVerbosity(Verbosity.ALL);
+               putDir.setMaxRetries(-1);
+               putDir.setEarlyEncode(useEarlyEncode);
+               putDir.setPriorityClass(priority);
+               putDir.setManifestPutter(manifestPutter);
+               for (ScannedFile file : files) {
+                       FileEntry fileEntry = createFileEntry(file, edition);
+                       if (fileEntry != null) {
+                               try {
+                                       putDir.addFileEntry(fileEntry);
+                               } catch (IOException ioe1) {
+                                       fireProjectInsertFinished(false, ioe1);
+                                       return;
+                               }
+                       }
+               }
+
+               /* start request */
+               try {
+                       client.execute(putDir, progressListener);
+                       fireProjectUploadFinished();
+               } catch (IOException ioe1) {
+                       fireProjectInsertFinished(false, ioe1);
+                       return;
+               }
+
+               /* parse progress and success messages */
+               String finalURI = null;
+               boolean success = false;
+               boolean finished = false;
+               boolean disconnected = false;
+               while (!finished && !cancelled) {
+                       Message message = client.readMessage();
+                       finished = (message == null) || (disconnected = client.isDisconnected());
+                       logger.log(Level.FINE, "Received message: " + message);
+                       if (!finished) {
+                               @SuppressWarnings("null")
+                               String messageName = message.getName();
+                               if ("URIGenerated".equals(messageName)) {
+                                       finalURI = message.get("URI");
+                                       fireProjectURIGenerated(finalURI);
+                               }
+                               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 && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error");
+                       }
+               }
+
+               /* post-insert work */
+               if (success) {
+                       @SuppressWarnings("null")
+                       String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1);
+                       int newEdition = Integer.parseInt(editionPart);
+                       project.setEdition(newEdition);
+                       project.setLastInsertionTime(System.currentTimeMillis());
+                       project.onSuccessfulInsert();
+               }
+               fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null));
+       }
+
+       //
+       // INTERFACE FileScannerListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void fileScannerFinished(FileScanner fileScanner) {
+               if (!fileScanner.isError()) {
+                       new Thread(this).start();
+               } else {
+                       fireProjectInsertFinished(false, null);
+               }
+               fileScanner.removeFileScannerListener(this);
+       }
+
+       /**
+        * Container class that collects all warnings and errors that occured during
+        * {@link ProjectInserter#validateProject(Project) project validation}.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public static class CheckReport implements Iterable<Issue> {
+
+               /** The issures that occured. */
+               private final List<Issue> issues = new ArrayList<Issue>();
+
+               /**
+                * Adds an issue.
+                *
+                * @param issue
+                *            The issue to add
+                */
+               public void addIssue(Issue issue) {
+                       issues.add(issue);
+               }
+
+               /**
+                * Creates an {@link Issue} from the given error key and fatality flag
+                * and {@link #addIssue(Issue) adds} it.
+                *
+                * @param errorKey
+                *            The error key
+                * @param fatal
+                *            {@code true} if the error is fatal, {@code false} if only
+                *            a warning should be generated
+                * @param parameters
+                *            Any additional parameters
+                */
+               public void addIssue(String errorKey, boolean fatal, String... parameters) {
+                       addIssue(new Issue(errorKey, fatal, parameters));
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               public Iterator<Issue> iterator() {
+                       return issues.iterator();
+               }
+
+               /**
+                * Returns whether this check report does not contain any errors.
+                *
+                * @return {@code true} if this check report does not contain any
+                *         errors, {@code false} if this check report does contain
+                *         errors
+                */
+               public boolean isEmpty() {
+                       return issues.isEmpty();
+               }
+
+               /**
+                * Returns the number of issues in this check report.
+                *
+                * @return The number of issues
+                */
+               public int size() {
+                       return issues.size();
+               }
+
+       }
+
+       /**
+        * Container class for a single issue. An issue contains an error key
+        * that describes the error, and a fatality flag that determines whether
+        * the insert has to be aborted (if the flag is {@code true}) or if it
+        * can still be performed and only a warning should be generated (if the
+        * flag is {@code false}).
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
+        *         Roden</a>
+        */
+       public static class Issue {
+
+               /** The error key. */
+               private final String errorKey;
+
+               /** The fatality flag. */
+               private final boolean fatal;
+
+               /** Additional parameters. */
+               private String[] parameters;
+
+               /**
+                * Creates a new issue.
+                *
+                * @param errorKey
+                *            The error key
+                * @param fatal
+                *            The fatality flag
+                * @param parameters
+                *            Any additional parameters
+                */
+               protected Issue(String errorKey, boolean fatal, String... parameters) {
+                       this.errorKey = errorKey;
+                       this.fatal = fatal;
+                       this.parameters = parameters;
+               }
+
+               /**
+                * Returns the key of the encountered error.
+                *
+                * @return The error key
+                */
+               public String getErrorKey() {
+                       return errorKey;
+               }
+
+               /**
+                * Returns whether the issue is fatal and the insert has to be
+                * aborted. Otherwise only a warning should be shown.
+                *
+                * @return {@code true} if the insert needs to be aborted, {@code
+                *         false} otherwise
+                */
+               public boolean isFatal() {
+                       return fatal;
+               }
+
+               /**
+                * Returns any additional parameters.
+                *
+                * @return The additional parameters
+                */
+               public String[] getParameters() {
+                       return parameters;
+               }
+
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/UpdateChecker.java b/src/main/java/de/todesbaum/jsite/application/UpdateChecker.java
new file mode 100644 (file)
index 0000000..c49f166
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * jSite - UpdateChecker.java - Copyright © 2008–2012 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 java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.todesbaum.jsite.main.Main;
+import de.todesbaum.jsite.main.Version;
+import de.todesbaum.util.freenet.fcp2.Client;
+import de.todesbaum.util.freenet.fcp2.ClientGet;
+import de.todesbaum.util.freenet.fcp2.Connection;
+import de.todesbaum.util.freenet.fcp2.Message;
+import de.todesbaum.util.freenet.fcp2.Persistence;
+import de.todesbaum.util.freenet.fcp2.ReturnType;
+import de.todesbaum.util.freenet.fcp2.Verbosity;
+import de.todesbaum.util.io.Closer;
+
+/**
+ * Checks for newer versions of jSite.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class UpdateChecker implements Runnable {
+
+       /** The logger. */
+       private static final Logger logger = Logger.getLogger(UpdateChecker.class.getName());
+
+       /** Counter for connection names. */
+       private static int counter = 0;
+
+       /** The edition for the update check URL. */
+       private static final int UPDATE_EDITION = 17;
+
+       /** The URL for update checks. */
+       private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE";
+
+       /** Object used for synchronization. */
+       private final Object syncObject = new Object();
+
+       /** Update listeners. */
+       private final List<UpdateListener> updateListeners = new ArrayList<UpdateListener>();
+
+       /** Whether the main thread should stop. */
+       private boolean shouldStop = false;
+
+       /** Current last found edition of update key. */
+       private int lastUpdateEdition = UPDATE_EDITION;
+
+       /** Last found version. */
+       private Version lastVersion = Main.getVersion();
+
+       /** The freenet interface. */
+       private final Freenet7Interface freenetInterface;
+
+       /**
+        * Creates a new update checker that uses the given frame as its parent and
+        * communications via the given freenet interface.
+        *
+        * @param freenetInterface
+        *            The freenet interface
+        */
+       public UpdateChecker(Freenet7Interface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       //
+       // EVENT LISTENER MANAGEMENT
+       //
+
+       /**
+        * Adds an update listener to the list of registered listeners.
+        *
+        * @param updateListener
+        *            The update listener to add
+        */
+       public void addUpdateListener(UpdateListener updateListener) {
+               updateListeners.add(updateListener);
+       }
+
+       /**
+        * Removes the given listener from the list of registered listeners.
+        *
+        * @param updateListener
+        *            The update listener to remove
+        */
+       public void removeUpdateListener(UpdateListener updateListener) {
+               updateListeners.remove(updateListener);
+       }
+
+       /**
+        * Notifies all listeners that a version was found.
+        *
+        * @param foundVersion
+        *            The version that was found
+        * @param versionTimestamp
+        *            The timestamp of the version
+        */
+       protected void fireUpdateFound(Version foundVersion, long versionTimestamp) {
+               for (UpdateListener updateListener : updateListeners) {
+                       updateListener.foundUpdateData(foundVersion, versionTimestamp);
+               }
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the latest version that was found.
+        *
+        * @return The latest found version
+        */
+       public Version getLatestVersion() {
+               return lastVersion;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Starts the update checker.
+        */
+       public void start() {
+               new Thread(this).start();
+       }
+
+       /**
+        * Stops the update checker.
+        */
+       public void stop() {
+               synchronized (syncObject) {
+                       shouldStop = true;
+                       syncObject.notifyAll();
+               }
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Returns whether the update checker should stop.
+        *
+        * @return <code>true</code> if the update checker should stop,
+        *         <code>false</code> otherwise
+        */
+       private boolean shouldStop() {
+               synchronized (syncObject) {
+                       return shouldStop;
+               }
+       }
+
+       /**
+        * Creates the URI of the update file for the given edition.
+        *
+        * @param edition
+        *            The edition number
+        * @return The URI for the update file for the given edition
+        */
+       private String constructUpdateKey(int edition) {
+               return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties";
+       }
+
+       //
+       // INTERFACE Runnable
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void run() {
+               Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker");
+               try {
+                       connection.connect();
+               } catch (IOException e1) {
+                       e1.printStackTrace();
+               }
+               Client client = new Client(connection);
+               boolean checkNow = false;
+               int currentEdition = lastUpdateEdition;
+               while (!shouldStop()) {
+                       checkNow = false;
+                       logger.log(Level.FINE, "Trying " + constructUpdateKey(currentEdition));
+                       ClientGet clientGet = new ClientGet("get-update-key");
+                       clientGet.setUri(constructUpdateKey(currentEdition));
+                       clientGet.setPersistence(Persistence.CONNECTION);
+                       clientGet.setReturnType(ReturnType.direct);
+                       clientGet.setVerbosity(Verbosity.ALL);
+                       try {
+                               client.execute(clientGet);
+                               boolean stop = false;
+                               while (!stop) {
+                                       Message message = client.readMessage();
+                                       logger.log(Level.FINEST, "Received message: " + message);
+                                       if (message == null) {
+                                               break;
+                                       }
+                                       if ("GetFailed".equals(message.getName())) {
+                                               if ("27".equals(message.get("code"))) {
+                                                       String editionString = message.get("redirecturi").split("/")[2];
+                                                       int editionNumber = -1;
+                                                       try {
+                                                               editionNumber = Integer.parseInt(editionString);
+                                                       } catch (NumberFormatException nfe1) {
+                                                               /* ignore. */
+                                                       }
+                                                       if (editionNumber != -1) {
+                                                               logger.log(Level.INFO, "Found new edition " + editionNumber);
+                                                               currentEdition = editionNumber;
+                                                               lastUpdateEdition = editionNumber;
+                                                               checkNow = true;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       if ("AllData".equals(message.getName())) {
+                                               logger.log(Level.FINE, "Update data found.");
+                                               InputStream dataInputStream = null;
+                                               Properties properties = new Properties();
+                                               try {
+                                                       dataInputStream = message.getPayloadInputStream();
+                                                       properties.load(dataInputStream);
+                                               } finally {
+                                                       Closer.close(dataInputStream);
+                                               }
+
+                                               String foundVersionString = properties.getProperty("jSite.Version");
+                                               if (foundVersionString != null) {
+                                                       Version foundVersion = Version.parse(foundVersionString);
+                                                       if (foundVersion != null) {
+                                                               lastVersion = foundVersion;
+                                                               String versionTimestampString = properties.getProperty("jSite.Date");
+                                                               logger.log(Level.FINEST, "Version timestamp: " + versionTimestampString);
+                                                               long versionTimestamp = -1;
+                                                               try {
+                                                                       versionTimestamp = Long.parseLong(versionTimestampString);
+                                                               } catch (NumberFormatException nfe1) {
+                                                                       /* ignore. */
+                                                               }
+                                                               fireUpdateFound(foundVersion, versionTimestamp);
+                                                               stop = true;
+                                                               checkNow = true;
+                                                               ++currentEdition;
+                                                       }
+                                               }
+                                       }
+                               }
+                       } catch (IOException e) {
+                               logger.log(Level.INFO, "Got IOException: " + e.getMessage());
+                               e.printStackTrace();
+                       }
+                       if (!checkNow && !shouldStop()) {
+                               synchronized (syncObject) {
+                                       try {
+                                               syncObject.wait(15 * 60 * 1000);
+                                       } catch (InterruptedException ie1) {
+                                               /* ignore. */
+                                       }
+                               }
+                       }
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/application/UpdateListener.java b/src/main/java/de/todesbaum/jsite/application/UpdateListener.java
new file mode 100644 (file)
index 0000000..a37440c
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * jSite - UpdateListener.java - Copyright © 2008–2012 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;
+
+import de.todesbaum.jsite.main.Version;
+
+/**
+ * Listener interface for objects that want to be notified when update data was
+ * found.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public interface UpdateListener extends EventListener {
+
+       /**
+        * Notifies a listener that data for the given version was found.
+        *
+        * @param foundVersion
+        *            The version that was found
+        * @param versionTimestamp
+        *            The timestamp of the version, or <code>-1</code> if the
+        *            timestamp is unknown
+        */
+       public void foundUpdateData(Version foundVersion, long versionTimestamp);
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/FileScanner.java b/src/main/java/de/todesbaum/jsite/gui/FileScanner.java
new file mode 100644 (file)
index 0000000..1790fbf
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * jSite - FileScanner.java - Copyright © 2006–2012 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.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.util.io.Closer;
+import de.todesbaum.util.io.StreamCopier;
+
+/**
+ * Scans the local path of a project anychronously and returns the list of found
+ * files as an event.
+ *
+ * @see Project#getLocalPath()
+ * @see FileScannerListener#fileScannerFinished(FileScanner)
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class FileScanner implements Runnable {
+
+       /** The logger. */
+       private final static Logger logger = Logger.getLogger(FileScanner.class.getName());
+
+       /** The list of listeners. */
+       private final List<FileScannerListener> fileScannerListeners = new ArrayList<FileScannerListener>();
+
+       /** The project to scan. */
+       private final Project project;
+
+       /** The list of found files. */
+       private List<ScannedFile> files;
+
+       /** Wether there was an error. */
+       private boolean error = false;
+
+       /**
+        * Creates a new file scanner for the given project.
+        *
+        * @param project
+        *            The project whose files to scan
+        */
+       public FileScanner(Project project) {
+               this.project = project;
+       }
+
+       /**
+        * Adds the given listener to the list of listeners.
+        *
+        * @param fileScannerListener
+        *            The listener to add
+        */
+       public void addFileScannerListener(FileScannerListener fileScannerListener) {
+               fileScannerListeners.add(fileScannerListener);
+       }
+
+       /**
+        * Removes the given listener from the list of listeners.
+        *
+        * @param fileScannerListener
+        *            The listener to remove
+        */
+       public void removeFileScannerListener(FileScannerListener fileScannerListener) {
+               fileScannerListeners.remove(fileScannerListener);
+       }
+
+       /**
+        * Notifies all listeners that the file scan finished.
+        */
+       protected void fireFileScannerFinished() {
+               for (FileScannerListener fileScannerListener : new ArrayList<FileScannerListener>(fileScannerListeners)) {
+                       fileScannerListener.fileScannerFinished(this);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * Scans all available files in the project’s local path and emits an event
+        * when finished.
+        *
+        * @see FileScannerListener#fileScannerFinished(FileScanner)
+        */
+       public void run() {
+               files = new ArrayList<ScannedFile>();
+               error = false;
+               try {
+                       scanFiles(new File(project.getLocalPath()), files);
+                       Collections.sort(files);
+               } catch (IOException ioe1) {
+                       error = true;
+               }
+               fireFileScannerFinished();
+       }
+
+       /**
+        * Returns whether there was an error scanning for files.
+        *
+        * @return <code>true</code> if there was an error, <code>false</code>
+        *         otherwise
+        */
+       public boolean isError() {
+               return error;
+       }
+
+       /**
+        * Returns the list of found files.
+        *
+        * @return The list of found files
+        */
+       public List<ScannedFile> getFiles() {
+               return files;
+       }
+
+       /**
+        * Recursively scans a directory and adds all found files to the given list.
+        *
+        * @param rootDir
+        *            The directory to scan
+        * @param fileList
+        *            The list to which to add the found files
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       private void scanFiles(File rootDir, List<ScannedFile> fileList) throws IOException {
+               File[] files = rootDir.listFiles(new FileFilter() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public boolean accept(File file) {
+                               return !project.isIgnoreHiddenFiles() || !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).replace('\\', '/');
+                       String hash = hashFile(project.getLocalPath(), filename);
+                       fileList.add(new ScannedFile(filename, hash));
+               }
+       }
+
+       /**
+        * Hashes the given file.
+        *
+        * @param path
+        *            The path of the project
+        * @param filename
+        *            The name of the file, relative to the project path
+        * @return The hash of the file
+        */
+       @SuppressWarnings("synthetic-access")
+       private static String hashFile(String path, String filename) {
+               InputStream fileInputStream = null;
+               DigestOutputStream digestOutputStream = null;
+               File file = new File(path, filename);
+               try {
+                       fileInputStream = new FileInputStream(file);
+                       digestOutputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256"));
+                       StreamCopier.copy(fileInputStream, digestOutputStream, file.length());
+                       return toHex(digestOutputStream.getMessageDigest().digest());
+               } catch (NoSuchAlgorithmException nsae1) {
+                       logger.log(Level.WARNING, "Could not get SHA-256 digest!", nsae1);
+               } catch (IOException ioe1) {
+                       logger.log(Level.WARNING, "Could not read file!", ioe1);
+               } finally {
+                       Closer.close(digestOutputStream);
+                       Closer.close(fileInputStream);
+               }
+               return toHex(new byte[32]);
+       }
+
+       /**
+        * Converts the given byte array into a hexadecimal string.
+        *
+        * @param array
+        *            The array to convert
+        * @return The hexadecimal string
+        */
+       private static String toHex(byte[] array) {
+               StringBuilder hexString = new StringBuilder(array.length * 2);
+               for (byte b : array) {
+                       hexString.append("0123456789abcdef".charAt((b >>> 4) & 0x0f)).append("0123456789abcdef".charAt(b & 0xf));
+               }
+               return hexString.toString();
+       }
+
+       /**
+        * {@link OutputStream} that discards all written bytes.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       private static class NullOutputStream extends OutputStream {
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public void write(int b) {
+                       /* do nothing. */
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public void write(byte[] b) {
+                       /* do nothing. */
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public void write(byte[] b, int off, int len) {
+                       /* do nothing. */
+               }
+
+       }
+
+       /**
+        * Container for a scanned file, consisting of the name of the file and its
+        * hash.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       public static class ScannedFile implements Comparable<ScannedFile> {
+
+               /** The name of the file. */
+               private final String filename;
+
+               /** The hash of the file. */
+               private final String hash;
+
+               /**
+                * Creates a new scanned file.
+                *
+                * @param filename
+                *            The name of the file
+                * @param hash
+                *            The hash of the file
+                */
+               public ScannedFile(String filename, String hash) {
+                       this.filename = filename;
+                       this.hash = hash;
+               }
+
+               //
+               // ACCESSORS
+               //
+
+               /**
+                * Returns the name of the file.
+                *
+                * @return The name of the file
+                */
+               public String getFilename() {
+                       return filename;
+               }
+
+               /**
+                * Returns the hash of the file.
+                *
+                * @return The hash of the file
+                */
+               public String getHash() {
+                       return hash;
+               }
+
+               //
+               // OBJECT METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public int hashCode() {
+                       return filename.hashCode();
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public boolean equals(Object obj) {
+                       return filename.equals(obj);
+               }
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public String toString() {
+                       return filename;
+               }
+
+               //
+               // COMPARABLE METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               public int compareTo(ScannedFile scannedFile) {
+                       return filename.compareTo(scannedFile.filename);
+               }
+
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java b/src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java
new file mode 100644 (file)
index 0000000..63e2c3d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * jSite - FileScannerListener.java - Copyright © 2006–2012 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;
+
+/**
+ * Listener interface for objects that want to be notified when scanning a
+ * project’s local path has finished.
+ *
+ * @see FileScanner
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public interface FileScannerListener extends EventListener {
+
+       /**
+        * Notifies a listener that scanning a project’s local path has finished.
+        *
+        * @param fileScanner
+        *            The file scanner that finished
+        */
+       public void fileScannerFinished(FileScanner fileScanner);
+
+}
\ No newline at end of file
diff --git a/src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java b/src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java
new file mode 100644 (file)
index 0000000..760c967
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * jSite - NodeManagerListener.java - Copyright © 2006–2012 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;
+
+/**
+ * Listener interface for objects that want to be notified if the node
+ * configuration changes.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public interface NodeManagerListener extends EventListener {
+
+       /**
+        * Notifies a listener that the node configuration was changed.
+        *
+        * @param nodes
+        *            The new list of nodes
+        */
+       public void nodesUpdated(Node[] nodes);
+
+       /**
+        * Notifies a listener that the selected node has changed.
+        *
+        * @param node
+        *            The new selected node
+        */
+       public void nodeSelected(Node node);
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java
new file mode 100644 (file)
index 0000000..8e44129
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * jSite - NodeManagerPage.java - Copyright © 2006–2012 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.jsite.i18n.I18nContainer;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * Wizard page that lets the user edit his nodes.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class NodeManagerPage extends TWizardPage implements ListSelectionListener, DocumentListener, ChangeListener {
+
+       /** List of node manager listeners. */
+       private List<NodeManagerListener> nodeManagerListeners = new ArrayList<NodeManagerListener>();
+
+       /** The “add node” action. */
+       protected Action addNodeAction;
+
+       /** The “delete node” action. */
+       protected Action deleteNodeAction;
+
+       /** The node list model. */
+       private DefaultListModel nodeListModel;
+
+       /** The node list. */
+       private JList nodeList;
+
+       /** The node name textfield. */
+       private JTextField nodeNameTextField;
+
+       /** The node hostname textfield. */
+       private JTextField nodeHostnameTextField;
+
+       /** The spinner for the node port. */
+       private JSpinner nodePortSpinner;
+
+       /**
+        * Creates a new node manager wizard page.
+        *
+        * @param wizard
+        *            The wizard this page belongs to
+        */
+       public NodeManagerPage(final TWizard wizard) {
+               super(wizard);
+               pageInit();
+               setHeading(I18n.getMessage("jsite.node-manager.heading"));
+               setDescription(I18n.getMessage("jsite.node-manager.description"));
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               setHeading(I18n.getMessage("jsite.node-manager.heading"));
+                               setDescription(I18n.getMessage("jsite.node-manager.description"));
+                       }
+               });
+       }
+
+       /**
+        * Adds a listener for node manager events.
+        *
+        * @param nodeManagerListener
+        *            The listener to add
+        */
+       public void addNodeManagerListener(NodeManagerListener nodeManagerListener) {
+               nodeManagerListeners.add(nodeManagerListener);
+       }
+
+       /**
+        * Removes a listener for node manager events.
+        *
+        * @param nodeManagerListener
+        *            The listener to remove
+        */
+       public void removeNodeManagerListener(NodeManagerListener nodeManagerListener) {
+               nodeManagerListeners.remove(nodeManagerListener);
+       }
+
+       /**
+        * Notifies all listeners that the node configuration has changed.
+        *
+        * @param nodes
+        *            The new list of nodes
+        */
+       protected void fireNodesUpdated(Node[] nodes) {
+               for (NodeManagerListener nodeManagerListener : nodeManagerListeners) {
+                       nodeManagerListener.nodesUpdated(nodes);
+               }
+       }
+
+       /**
+        * Notifies all listeners that a new node was selected.
+        *
+        * @param node
+        *            The newly selected node
+        */
+       protected void fireNodeSelected(Node node) {
+               for (NodeManagerListener nodeManagerListener : nodeManagerListeners) {
+                       nodeManagerListener.nodeSelected(node);
+               }
+       }
+
+       /**
+        * Creates all actions.
+        */
+       private void createActions() {
+               addNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.add-node")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               addNode();
+                       }
+               };
+
+               deleteNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.delete-node")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               deleteNode();
+                       }
+               };
+               deleteNodeAction.setEnabled(false);
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               addNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.add-node"));
+                               deleteNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.delete-node"));
+                       }
+               });
+       }
+
+       /**
+        * Initializes the page and all components in it.
+        */
+       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));
+               final JLabel nodeInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.node-manager.node-information") + "</b></html>");
+               nodeInformationPanel.add(nodeInformationLabel, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0));
+               final TLabel nodeNameLabel = new TLabel(I18n.getMessage("jsite.node-manager.name") + ":", KeyEvent.VK_N, nodeNameTextField);
+               nodeInformationPanel.add(nodeNameLabel, 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));
+               final TLabel nodeHostnameLabel = new TLabel(I18n.getMessage("jsite.node-manager.hostname") + ":", KeyEvent.VK_H, nodeHostnameTextField);
+               nodeInformationPanel.add(nodeHostnameLabel, 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));
+               final TLabel nodePortLabel = new TLabel(I18n.getMessage("jsite.node-manager.port") + ":", KeyEvent.VK_P, nodePortSpinner);
+               nodeInformationPanel.add(nodePortLabel, 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);
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               nodeInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.node-manager.node-information") + "</b></html>");
+                               nodeNameLabel.setText(I18n.getMessage("jsite.node-manager.name") + ":");
+                               nodeHostnameLabel.setText(I18n.getMessage("jsite.node-manager.hostname") + ":");
+                               nodePortLabel.setText(I18n.getMessage("jsite.node-manager.port") + ":");
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               this.wizard.setNextEnabled(nodeListModel.getSize() > 0);
+               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+       }
+
+       /**
+        * Sets the node list.
+        *
+        * @param nodes
+        *            The list of nodes
+        */
+       public void setNodes(Node[] nodes) {
+               nodeListModel.clear();
+               for (Node node : nodes) {
+                       nodeListModel.addElement(node);
+               }
+               nodeList.repaint();
+               fireNodesUpdated(nodes);
+       }
+
+       /**
+        * Returns the node list.
+        *
+        * @return The list of 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;
+       }
+
+       /**
+        * Returns the currently selected node.
+        *
+        * @return The selected node, or <code>null</code> if no node is selected
+        */
+       private Node getSelectedNode() {
+               return (Node) nodeList.getSelectedValue();
+       }
+
+       /**
+        * Updates node name or hostname when the user types into the textfields.
+        *
+        * @see #insertUpdate(DocumentEvent)
+        * @see #removeUpdate(DocumentEvent)
+        * @see #changedUpdate(DocumentEvent)
+        * @see DocumentListener
+        * @param documentEvent
+        *            The document event
+        */
+       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) {
+                       /* ignore. */
+               }
+               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();
+                       fireNodesUpdated(getNodes());
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Adds a new node to the list of nodes.
+        */
+       private void addNode() {
+               Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node"));
+               nodeListModel.addElement(node);
+               deleteNodeAction.setEnabled(nodeListModel.size() > 1);
+               wizard.setNextEnabled(true);
+               fireNodesUpdated(getNodes());
+       }
+
+       /**
+        * Deletes the currently selected node from the list of nodes.
+        */
+       private 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;
+               }
+               int nodeIndex = nodeListModel.indexOf(node);
+               nodeListModel.removeElement(node);
+               nodeList.repaint();
+               fireNodeSelected((Node) nodeListModel.get(Math.min(nodeIndex, nodeListModel.size() - 1)));
+               fireNodesUpdated(getNodes());
+               deleteNodeAction.setEnabled(nodeListModel.size() > 1);
+               wizard.setNextEnabled(nodeListModel.size() > 0);
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @SuppressWarnings("null")
+       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 && (nodeListModel.size() > 1));
+                               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());
+                               fireNodeSelected(selectedNode);
+                               nodeList.repaint();
+                       }
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java b/src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java
new file mode 100644 (file)
index 0000000..5cf258a
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ * jSite - PreferencesPage.java - Copyright © 2009–2012 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.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.jsite.i18n.I18nContainer;
+import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
+import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
+import de.todesbaum.util.freenet.fcp2.PriorityClass;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * Page that shows some preferences that are valid for the complete application.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class PreferencesPage extends TWizardPage {
+
+       /** Select default temp directory action. */
+       private Action selectDefaultTempDirectoryAction;
+
+       /** Select custom temp directory action. */
+       private Action selectCustomTempDirectoryAction;
+
+       /** Action that chooses a new temp directory. */
+       private Action chooseTempDirectoryAction;
+
+       /** Action when selecting “next to JAR file.” */
+       private Action nextToJarFileAction;
+
+       /** Action when selecting “home directory.” */
+       private Action homeDirectoryAction;
+
+       /** Action when selecting “custom directory.” */
+       private Action customDirectoryAction;
+
+       /** Action when selecting “use early encode.” */
+       private Action useEarlyEncodeAction;
+
+       /** Action when a priority was selected. */
+       private Action priorityAction;
+
+       /** The text field containing the directory. */
+       private JTextField tempDirectoryTextField;
+
+       /** The temp directory. */
+       private String tempDirectory;
+
+       /** The configuration location. */
+       private ConfigurationLocation configurationLocation;
+
+       /** Whether to use “early encode.” */
+       private boolean useEarlyEncode;
+
+       /** The prioriy for inserts. */
+       private PriorityClass priority;
+
+       /** The “default” button. */
+       private JRadioButton defaultTempDirectory;
+
+       /** The “custom” button. */
+       private JRadioButton customTempDirectory;
+
+       /** The “next to JAR file” checkbox. */
+       private JRadioButton nextToJarFile;
+
+       /** The “home directory” checkbox. */
+       private JRadioButton homeDirectory;
+
+       /** The “custom directory” checkbox. */
+       private JRadioButton customDirectory;
+
+       /** The “use early encode” checkbox. */
+       private JCheckBox useEarlyEncodeCheckBox;
+
+       /** The insert priority select box. */
+       private JComboBox insertPriorityComboBox;
+
+       /** The manifest putter select box. */
+       private JComboBox manifestPutterComboBox;
+
+       /**
+        * Creates a new “preferences” page.
+        *
+        * @param wizard
+        *            The wizard this page belongs to
+        */
+       public PreferencesPage(TWizard wizard) {
+               super(wizard);
+               pageInit();
+               setHeading(I18n.getMessage("jsite.preferences.heading"));
+               setDescription(I18n.getMessage("jsite.preferences.description"));
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       public void run() {
+                               setHeading(I18n.getMessage("jsite.preferences.heading"));
+                               setDescription(I18n.getMessage("jsite.preferences.description"));
+                       }
+               });
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the temp directory.
+        *
+        * @return The temp directory, or {@code null} to use the default temp
+        *         directory
+        */
+       public String getTempDirectory() {
+               return tempDirectory;
+       }
+
+       /**
+        * Sets the temp directory.
+        *
+        * @param tempDirectory
+        *            The temp directory, or {@code null} to use the default temp
+        *            directory
+        */
+       public void setTempDirectory(String tempDirectory) {
+               this.tempDirectory = tempDirectory;
+               tempDirectoryTextField.setText((tempDirectory != null) ? tempDirectory : "");
+               if (tempDirectory != null) {
+                       customTempDirectory.setSelected(true);
+                       chooseTempDirectoryAction.setEnabled(true);
+               } else {
+                       defaultTempDirectory.setSelected(true);
+               }
+       }
+
+       /**
+        * Returns the configuration location.
+        *
+        * @return The configuration location
+        */
+       public ConfigurationLocation getConfigurationLocation() {
+               return configurationLocation;
+       }
+
+       /**
+        * Sets the configuration location.
+        *
+        * @param configurationLocation
+        *            The configuration location
+        */
+       public void setConfigurationLocation(ConfigurationLocation configurationLocation) {
+               this.configurationLocation = configurationLocation;
+               switch (configurationLocation) {
+               case NEXT_TO_JAR_FILE:
+                       nextToJarFile.setSelected(true);
+                       break;
+               case HOME_DIRECTORY:
+                       homeDirectory.setSelected(true);
+                       break;
+               case CUSTOM:
+                       customDirectory.setSelected(true);
+                       break;
+               }
+       }
+
+       /**
+        * Sets whether it is possible to select the “next to JAR file” option for
+        * the configuration location.
+        *
+        * @param nextToJarFile
+        *            {@code true} if the configuration file can be saved next to
+        *            the JAR file, {@code false} otherwise
+        */
+       public void setHasNextToJarConfiguration(boolean nextToJarFile) {
+               this.nextToJarFile.setEnabled(nextToJarFile);
+       }
+
+       /**
+        * Sets whether it is possible to select the “custom location” option for
+        * the configuration location.
+        *
+        * @param customDirectory
+        *            {@code true} if the configuration file can be saved to a
+        *            custom location, {@code false} otherwise
+        */
+       public void setHasCustomConfiguration(boolean customDirectory) {
+               this.customDirectory.setEnabled(customDirectory);
+       }
+
+       /**
+        * Returns whether to use the “early encode“ flag for the insert.
+        *
+        * @return {@code true} to set the “early encode” flag for the insert,
+        *         {@code false} otherwise
+        */
+       public boolean useEarlyEncode() {
+               return useEarlyEncode;
+       }
+
+       /**
+        * Sets whether to use the “early encode“ flag for the insert.
+        *
+        * @param useEarlyEncode
+        *            {@code true} to set the “early encode” flag for the insert,
+        *            {@code false} otherwise
+        */
+       public void setUseEarlyEncode(boolean useEarlyEncode) {
+               useEarlyEncodeCheckBox.setSelected(useEarlyEncode);
+       }
+
+       /**
+        * Returns the configured insert priority.
+        *
+        * @return The insert priority
+        */
+       public PriorityClass getPriority() {
+               return priority;
+       }
+
+       /**
+        * Sets the insert priority.
+        *
+        * @param priority
+        *            The insert priority
+        */
+       public void setPriority(PriorityClass priority) {
+               insertPriorityComboBox.setSelectedItem(priority);
+       }
+
+       /**
+        * Returns the selected manifest putter.
+        *
+        * @return The selected manifest putter
+        */
+       public ManifestPutter getManifestPutter() {
+               return (ManifestPutter) manifestPutterComboBox.getSelectedItem();
+       }
+
+       /**
+        * Sets the manifest putter.
+        *
+        * @param manifestPutter
+        *            The manifest putter
+        */
+       public void setManifestPutter(ManifestPutter manifestPutter) {
+               manifestPutterComboBox.setSelectedItem(manifestPutter);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               super.pageAdded(wizard);
+               this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes"));
+               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+               this.wizard.setNextEnabled(false);
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Initializes this page.
+        */
+       private void pageInit() {
+               createActions();
+               setLayout(new BorderLayout(12, 12));
+               add(createPreferencesPanel(), BorderLayout.CENTER);
+       }
+
+       /**
+        * Creates all actions.
+        */
+       private void createActions() {
+               selectDefaultTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.default")) {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               selectDefaultTempDirectory();
+                       }
+               };
+               selectCustomTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.custom")) {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               selectCustomTempDirectory();
+                       }
+               };
+               chooseTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.choose")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent e) {
+                               chooseTempDirectory();
+                       }
+               };
+               nextToJarFileAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.jar")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionevent) {
+                               configurationLocation = ConfigurationLocation.NEXT_TO_JAR_FILE;
+                       }
+               };
+               homeDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.home")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionevent) {
+                               configurationLocation = ConfigurationLocation.HOME_DIRECTORY;
+                       }
+               };
+               customDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.custom")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               configurationLocation = ConfigurationLocation.CUSTOM;
+                       }
+               };
+               useEarlyEncodeAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.use-early-encode")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               useEarlyEncode = useEarlyEncodeCheckBox.isSelected();
+                       }
+               };
+               priorityAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.priority")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               priority = (PriorityClass) insertPriorityComboBox.getSelectedItem();
+                       }
+               };
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               selectDefaultTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.default"));
+                               selectCustomTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.custom"));
+                               chooseTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.choose"));
+                               nextToJarFileAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.jar"));
+                               homeDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.home"));
+                               customDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.custom"));
+                               useEarlyEncodeAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.insert-options.use-early-encode"));
+                       }
+               });
+       }
+
+       /**
+        * Creates the panel containing all preferences.
+        *
+        * @return The preferences panel
+        */
+       private JPanel createPreferencesPanel() {
+               JPanel preferencesPanel = new JPanel(new GridBagLayout());
+               preferencesPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
+
+               final JLabel tempDirectoryLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.temp-directory") + "</b></html>");
+               preferencesPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
+
+               defaultTempDirectory = new JRadioButton(selectDefaultTempDirectoryAction);
+               preferencesPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0));
+
+               customTempDirectory = new JRadioButton(selectCustomTempDirectoryAction);
+               preferencesPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
+
+               ButtonGroup tempDirectoryButtonGroup = new ButtonGroup();
+               defaultTempDirectory.getModel().setGroup(tempDirectoryButtonGroup);
+               customTempDirectory.getModel().setGroup(tempDirectoryButtonGroup);
+
+               tempDirectoryTextField = new JTextField();
+               tempDirectoryTextField.setEditable(false);
+               if (tempDirectory != null) {
+                       tempDirectoryTextField.setText(tempDirectory);
+                       customTempDirectory.setSelected(true);
+               } else {
+                       defaultTempDirectory.setSelected(true);
+               }
+               chooseTempDirectoryAction.setEnabled(tempDirectory != null);
+               preferencesPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0));
+
+               JButton chooseButton = new JButton(chooseTempDirectoryAction);
+               preferencesPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0));
+
+               final JLabel configurationDirectoryLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.config-directory") + "</b></html>");
+               preferencesPanel.add(configurationDirectoryLabel, new GridBagConstraints(0, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 0, 0, 0), 0, 0));
+
+               nextToJarFile = new JRadioButton(nextToJarFileAction);
+               preferencesPanel.add(nextToJarFile, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0));
+
+               homeDirectory = new JRadioButton(homeDirectoryAction);
+               preferencesPanel.add(homeDirectory, new GridBagConstraints(0, 5, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
+
+               customDirectory = new JRadioButton(customDirectoryAction);
+               preferencesPanel.add(customDirectory, new GridBagConstraints(0, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0));
+
+               ButtonGroup configurationDirectoryButtonGroup = new ButtonGroup();
+               configurationDirectoryButtonGroup.add(nextToJarFile);
+               configurationDirectoryButtonGroup.add(homeDirectory);
+               configurationDirectoryButtonGroup.add(customDirectory);
+
+               final JLabel insertOptionsLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.preferences.insert-options") + "</b></html>");
+               preferencesPanel.add(insertOptionsLabel, new GridBagConstraints(0, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
+
+               useEarlyEncodeCheckBox = new JCheckBox(useEarlyEncodeAction);
+               preferencesPanel.add(useEarlyEncodeCheckBox, new GridBagConstraints(0, 8, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               final JLabel insertPriorityLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.priority"));
+               preferencesPanel.add(insertPriorityLabel, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               insertPriorityComboBox = new JComboBox(new PriorityClass[] { PriorityClass.MINIMUM, PriorityClass.PREFETCH, PriorityClass.BULK, PriorityClass.UPDATABLE, PriorityClass.SEMI_INTERACTIVE, PriorityClass.INTERACTIVE, PriorityClass.MAXIMUM });
+               insertPriorityComboBox.setAction(priorityAction);
+               preferencesPanel.add(insertPriorityComboBox, new GridBagConstraints(1, 9, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0));
+
+               final JLabel manifestPutterLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.manifest-putter"));
+               preferencesPanel.add(manifestPutterLabel, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               manifestPutterComboBox = new JComboBox(ManifestPutter.values());
+               preferencesPanel.add(manifestPutterComboBox, new GridBagConstraints(1, 10, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       public void run() {
+                               tempDirectoryLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.temp-directory") + "</b></html>");
+                               configurationDirectoryLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.config-directory") + "</b></html>");
+                               insertOptionsLabel.setText("<html><b>" + I18n.getMessage("jsite.preferences.insert-options") + "</b></html>");
+                               insertPriorityLabel.setText(I18n.getMessage("jsite.preferences.insert-options.priority"));
+                               manifestPutterLabel.setText(I18n.getMessage("jsite.preferences.insert-options.manifest-putter"));
+                       }
+               });
+
+               return preferencesPanel;
+       }
+
+       /**
+        * Activates the default temp directory radio button.
+        */
+       private void selectDefaultTempDirectory() {
+               tempDirectoryTextField.setEnabled(false);
+               chooseTempDirectoryAction.setEnabled(false);
+               tempDirectory = null;
+       }
+
+       /**
+        * Activates the custom temp directory radio button.
+        */
+       private void selectCustomTempDirectory() {
+               tempDirectoryTextField.setEnabled(true);
+               chooseTempDirectoryAction.setEnabled(true);
+               if (tempDirectoryTextField.getText().length() == 0) {
+                       chooseTempDirectory();
+                       if (tempDirectoryTextField.getText().length() == 0) {
+                               defaultTempDirectory.setSelected(true);
+                       }
+               }
+       }
+
+       /**
+        * Lets the user choose a new temp directory.
+        */
+       private void chooseTempDirectory() {
+               JFileChooser fileChooser = new JFileChooser(tempDirectory);
+               fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+               int returnValue = fileChooser.showDialog(wizard, I18n.getMessage("jsite.preferences.temp-directory.choose.approve"));
+               if (returnValue == JFileChooser.CANCEL_OPTION) {
+                       return;
+               }
+               tempDirectory = fileChooser.getSelectedFile().getPath();
+               tempDirectoryTextField.setText(tempDirectory);
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java
new file mode 100644 (file)
index 0000000..80bb52c
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * jSite - ProjectFilesPage.java - Copyright © 2006–2012 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.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.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+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.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+import de.todesbaum.jsite.application.FileOption;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.gui.FileScanner.ScannedFile;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.jsite.i18n.I18nContainer;
+import de.todesbaum.util.mime.DefaultMIMETypes;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * Wizard page that lets the user manage the files of a project.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener {
+
+       /** The project. */
+       private Project project;
+
+       /** The “scan files” action. */
+       private Action scanAction;
+
+       /** The “ignore hidden files” checkbox. */
+       private JCheckBox ignoreHiddenFilesCheckBox;
+
+       /** The list of project files. */
+       private JList projectFileList;
+
+       /** The “default file” checkbox. */
+       private JCheckBox defaultFileCheckBox;
+
+       /** The “insert” checkbox. */
+       private JCheckBox fileOptionsInsertCheckBox;
+
+       /** The “force insert” checkbox. */
+       private JCheckBox fileOptionsForceInsertCheckBox;
+
+       /** The “insert redirect” checkbox. */
+       private JCheckBox fileOptionsInsertRedirectCheckBox;
+
+       /** The “custom key” textfield. */
+       private JTextField fileOptionsCustomKeyTextField;
+
+       /** The “rename” check box. */
+       private JCheckBox fileOptionsRenameCheckBox;
+
+       /** The “new name” text field. */
+       private JTextField fileOptionsRenameTextField;
+
+       /** The “mime type” combo box. */
+       private JComboBox fileOptionsMIMETypeComboBox;
+
+       /**
+        * Creates a new project file page.
+        *
+        * @param wizard
+        *            The wizard the page belongs to
+        */
+       public ProjectFilesPage(final TWizard wizard) {
+               super(wizard);
+               pageInit();
+       }
+
+       /**
+        * Initializes the page and all its actions and components.
+        */
+       private void pageInit() {
+               createActions();
+               setLayout(new BorderLayout(12, 12));
+               add(createProjectFilesPanel(), BorderLayout.CENTER);
+       }
+
+       /**
+        * Creates all actions.
+        */
+       private void createActions() {
+               scanAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.rescan")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       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"));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               scanAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.rescan"));
+                               scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip"));
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               actionScan();
+               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               this.wizard.setNextName(I18n.getMessage("jsite.project-files.insert-now"));
+               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+       }
+
+       /**
+        * Creates the panel contains the project file list and options.
+        *
+        * @return The created panel
+        */
+       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);
+
+               ignoreHiddenFilesCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.ignore-hidden-files"));
+               ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.ignore-hidden-files.tooltip"));
+               ignoreHiddenFilesCheckBox.setName("ignore-hidden-files");
+               ignoreHiddenFilesCheckBox.addActionListener(this);
+               fileOptionsPanel.add(ignoreHiddenFilesCheckBox, 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 JButton(scanAction), new GridBagConstraints(0, 1, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0));
+
+               final JLabel fileOptionsLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project-files.file-options") + "</b></html>");
+               fileOptionsPanel.add(fileOptionsLabel, new GridBagConstraints(0, 2, 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, 3, 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, 4, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+
+               fileOptionsForceInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.force-insert"));
+               fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip"));
+               fileOptionsForceInsertCheckBox.setName("force-insert");
+               fileOptionsForceInsertCheckBox.setMnemonic(KeyEvent.VK_F);
+               fileOptionsForceInsertCheckBox.addActionListener(this);
+               fileOptionsForceInsertCheckBox.setEnabled(false);
+
+               fileOptionsPanel.add(fileOptionsForceInsertCheckBox, new GridBagConstraints(0, 5, 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);
+
+               fileOptionsInsertRedirectCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert-redirect"), false);
+               fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip"));
+               fileOptionsInsertRedirectCheckBox.setName("insert-redirect");
+               fileOptionsInsertRedirectCheckBox.setMnemonic(KeyEvent.VK_R);
+               fileOptionsInsertRedirectCheckBox.addActionListener(this);
+               fileOptionsInsertRedirectCheckBox.setEnabled(false);
+
+               final TLabel customKeyLabel = new TLabel(I18n.getMessage("jsite.project-files.custom-key") + ":", KeyEvent.VK_K, fileOptionsCustomKeyTextField);
+               fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, 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(customKeyLabel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0));
+               fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               fileOptionsRenameCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.rename"), false);
+               fileOptionsRenameCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.rename.tooltip"));
+               fileOptionsRenameCheckBox.setName("rename");
+               fileOptionsRenameCheckBox.setMnemonic(KeyEvent.VK_N);
+               fileOptionsRenameCheckBox.addActionListener(this);
+               fileOptionsRenameCheckBox.setEnabled(false);
+
+               fileOptionsRenameTextField = new JTextField();
+               fileOptionsRenameTextField.setEnabled(false);
+               fileOptionsRenameTextField.getDocument().addDocumentListener(new DocumentListener() {
+
+                       @SuppressWarnings("synthetic-access")
+                       private void storeText(DocumentEvent documentEvent) {
+                               FileOption fileOption = getSelectedFile();
+                               if (fileOption == null) {
+                                       /* no file selected. */
+                                       return;
+                               }
+                               Document document = documentEvent.getDocument();
+                               int documentLength = document.getLength();
+                               try {
+                                       fileOption.setChangedName(document.getText(0, documentLength).trim());
+                               } catch (BadLocationException ble1) {
+                                       /* ignore, it should never happen. */
+                               }
+                       }
+
+                       public void changedUpdate(DocumentEvent documentEvent) {
+                               storeText(documentEvent);
+                       }
+
+                       public void insertUpdate(DocumentEvent documentEvent) {
+                               storeText(documentEvent);
+                       }
+
+                       public void removeUpdate(DocumentEvent documentEvent) {
+                               storeText(documentEvent);
+                       }
+
+               });
+
+               fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0));
+               fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 7, 3, 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.setEditable(true);
+               fileOptionsMIMETypeComboBox.setEnabled(false);
+
+               final TLabel mimeTypeLabel = new TLabel(I18n.getMessage("jsite.project-files.mime-type") + ":", KeyEvent.VK_M, fileOptionsMIMETypeComboBox);
+               fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 8, 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, 8, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               ignoreHiddenFilesCheckBox.setText(I18n.getMessage("jsite.project-files.ignore-hidden-files"));
+                               ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.projet-files.ignore-hidden-files.tooltip"));
+                               fileOptionsLabel.setText("<html><b>" + I18n.getMessage("jsite.project-files.file-options") + "</b></html>");
+                               defaultFileCheckBox.setText(I18n.getMessage("jsite.project-files.default"));
+                               defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip"));
+                               fileOptionsInsertCheckBox.setText(I18n.getMessage("jsite.project-files.insert"));
+                               fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip"));
+                               fileOptionsForceInsertCheckBox.setText(I18n.getMessage("jsite.project-files.force-insert"));
+                               fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip"));
+                               fileOptionsInsertRedirectCheckBox.setText(I18n.getMessage("jsite.project-files.insert-redirect"));
+                               fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip"));
+                               fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip"));
+                               customKeyLabel.setText(I18n.getMessage("jsite.project-files.custom-key") + ":");
+                               fileOptionsRenameCheckBox.setText("jsite.project-files.rename");
+                               fileOptionsRenameCheckBox.setToolTipText("jsite.project-files.rename.tooltip");
+                               fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip"));
+                               mimeTypeLabel.setText(I18n.getMessage("jsite.project-files.mime-type") + ":");
+                       }
+               });
+
+               return projectFilesPanel;
+       }
+
+       /**
+        * Sets the project whose files to manage.
+        *
+        * @param project
+        *            The project whose files to manage
+        */
+       public void setProject(final Project project) {
+               this.project = project;
+               setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName()));
+               setDescription(I18n.getMessage("jsite.project-files.description"));
+               ignoreHiddenFilesCheckBox.setSelected(project.isIgnoreHiddenFiles());
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName()));
+                               setDescription(I18n.getMessage("jsite.project-files.description"));
+                       }
+               });
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Rescans the project’s files.
+        */
+       private 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();
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * Updates the file list.
+        */
+       public void fileScannerFinished(FileScanner fileScanner) {
+               final boolean error = fileScanner.isError();
+               if (!error) {
+                       final List<ScannedFile> files = fileScanner.getFiles();
+                       SwingUtilities.invokeLater(new Runnable() {
+
+                               @SuppressWarnings("synthetic-access")
+                               public void run() {
+                                       projectFileList.setListData(files.toArray());
+                                       projectFileList.clearSelection();
+                               }
+                       });
+                       Set<String> entriesToRemove = new HashSet<String>();
+                       Iterator<String> filenames = new HashSet<String>(project.getFileOptions().keySet()).iterator();
+                       while (filenames.hasNext()) {
+                               String filename = filenames.next();
+                               boolean found = false;
+                               for (ScannedFile scannedFile : files) {
+                                       if (scannedFile.getFilename().equals(filename)) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if (!found) {
+                                       entriesToRemove.add(filename);
+                               }
+                       }
+                       for (String filename : entriesToRemove) {
+                               project.setFileOption(filename, null);
+                       }
+               } else {
+                       JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.scan-error"), null, JOptionPane.ERROR_MESSAGE);
+               }
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               wizard.setPreviousEnabled(true);
+                               wizard.setNextEnabled(!error);
+                               wizard.setQuitEnabled(true);
+                       }
+               });
+       }
+
+       /**
+        * Returns the {@link FileOption file options} for the currently selected
+        * file.
+        *
+        * @return The {@link FileOption}s for the selected file, or {@code null} if
+        *         no file is selected
+        */
+       private FileOption getSelectedFile() {
+               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
+               if (scannedFile == null) {
+                       return null;
+               }
+               return project.getFileOption(scannedFile.getFilename());
+       }
+
+       //
+       // INTERFACE ActionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void actionPerformed(ActionEvent actionEvent) {
+               Object source = actionEvent.getSource();
+               if ((source instanceof JCheckBox) && ("ignore-hidden-files".equals(((JCheckBox) source).getName()))) {
+                       project.setIgnoreHiddenFiles(((JCheckBox) source).isSelected());
+                       actionScan();
+                       return;
+               }
+               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
+               if (scannedFile == null) {
+                       return;
+               }
+               String filename = scannedFile.getFilename();
+               FileOption fileOption = project.getFileOption(filename);
+               if (source instanceof JCheckBox) {
+                       JCheckBox checkBox = (JCheckBox) source;
+                       if ("default-file".equals(checkBox.getName())) {
+                               if (checkBox.isSelected()) {
+                                       if (filename.indexOf('/') > -1) {
+                                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.invalid-default-file"), null, JOptionPane.ERROR_MESSAGE);
+                                               checkBox.setSelected(false);
+                                       } else {
+                                               project.setIndexFile(filename);
+                                       }
+                               } else {
+                                       if (filename.equals(project.getIndexFile())) {
+                                               project.setIndexFile(null);
+                                       }
+                               }
+                       } else if ("insert".equals(checkBox.getName())) {
+                               boolean isInsert = checkBox.isSelected();
+                               fileOption.setInsert(isInsert);
+                               fileOptionsInsertRedirectCheckBox.setEnabled(!isInsert);
+                       } else if ("force-insert".equals(checkBox.getName())) {
+                               boolean isForceInsert = checkBox.isSelected();
+                               fileOption.setForceInsert(isForceInsert);
+                       } else if ("insert-redirect".equals(checkBox.getName())) {
+                               boolean isInsertRedirect = checkBox.isSelected();
+                               fileOption.setInsertRedirect(isInsertRedirect);
+                               fileOptionsCustomKeyTextField.setEnabled(isInsertRedirect);
+                       } else if ("rename".equals(checkBox.getName())) {
+                               boolean isRenamed = checkBox.isSelected();
+                               fileOptionsRenameTextField.setEnabled(isRenamed);
+                               fileOption.setChangedName(isRenamed ? fileOptionsRenameTextField.getText() : "");
+                       }
+               } else if (source instanceof JComboBox) {
+                       JComboBox comboBox = (JComboBox) source;
+                       if ("project-files.mime-type".equals(comboBox.getName())) {
+                               fileOption.setMimeType((String) comboBox.getSelectedItem());
+                       }
+               }
+       }
+
+       //
+       // INTERFACE ListSelectionListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @SuppressWarnings("null")
+       public void valueChanged(ListSelectionEvent e) {
+               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
+               boolean enabled = scannedFile != null;
+               String filename = (scannedFile == null) ? null : scannedFile.getFilename();
+               defaultFileCheckBox.setEnabled(enabled);
+               fileOptionsInsertCheckBox.setEnabled(enabled);
+               fileOptionsRenameCheckBox.setEnabled(enabled);
+               fileOptionsMIMETypeComboBox.setEnabled(enabled);
+               if (filename != null) {
+                       FileOption fileOption = project.getFileOption(filename);
+                       defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile()));
+                       fileOptionsInsertCheckBox.setSelected(fileOption.isInsert());
+                       fileOptionsForceInsertCheckBox.setEnabled(scannedFile.getHash().equals(fileOption.getLastInsertHash()));
+                       fileOptionsForceInsertCheckBox.setSelected(fileOption.isForceInsert());
+                       fileOptionsInsertRedirectCheckBox.setEnabled(!fileOption.isInsert());
+                       fileOptionsInsertRedirectCheckBox.setSelected(fileOption.isInsertRedirect());
+                       fileOptionsCustomKeyTextField.setEnabled(fileOption.isInsertRedirect());
+                       fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey());
+                       fileOptionsRenameCheckBox.setSelected(fileOption.hasChangedName());
+                       fileOptionsRenameTextField.setEnabled(fileOption.hasChangedName());
+                       fileOptionsRenameTextField.setText(fileOption.getChangedName());
+                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType());
+               } else {
+                       defaultFileCheckBox.setSelected(false);
+                       fileOptionsInsertCheckBox.setSelected(true);
+                       fileOptionsForceInsertCheckBox.setEnabled(false);
+                       fileOptionsForceInsertCheckBox.setSelected(false);
+                       fileOptionsInsertRedirectCheckBox.setEnabled(false);
+                       fileOptionsInsertRedirectCheckBox.setSelected(false);
+                       fileOptionsCustomKeyTextField.setEnabled(false);
+                       fileOptionsCustomKeyTextField.setText("CHK@");
+                       fileOptionsRenameCheckBox.setEnabled(false);
+                       fileOptionsRenameCheckBox.setSelected(false);
+                       fileOptionsRenameTextField.setEnabled(false);
+                       fileOptionsRenameTextField.setText("");
+                       fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE);
+               }
+       }
+
+       //
+       // INTERFACE DocumentListener
+       //
+
+       /**
+        * Updates the options of the currently selected file with the changes made
+        * in the “custom key” textfield.
+        *
+        * @param documentEvent
+        *            The document event to process
+        */
+       private void processDocumentUpdate(DocumentEvent documentEvent) {
+               ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue();
+               if (scannedFile == null) {
+                       return;
+               }
+               FileOption fileOption = project.getFileOption(scannedFile.getFilename());
+               Document document = documentEvent.getDocument();
+               try {
+                       String text = document.getText(0, document.getLength());
+                       fileOption.setCustomKey(text);
+               } catch (BadLocationException ble1) {
+                       /* ignore. */
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void changedUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void insertUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void removeUpdate(DocumentEvent documentEvent) {
+               processDocumentUpdate(documentEvent);
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java
new file mode 100644 (file)
index 0000000..bfd8b89
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * jSite - ProjectInsertPage.java - Copyright © 2006–2012 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.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.ClipboardOwner;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+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.AbortedException;
+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.jsite.i18n.I18nContainer;
+import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
+import de.todesbaum.util.freenet.fcp2.PriorityClass;
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * Wizard page that shows the progress of an insert.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ProjectInsertPage extends TWizardPage implements InsertListener, ClipboardOwner {
+
+       /** The logger. */
+       private static final Logger logger = Logger.getLogger(ProjectInsertPage.class.getName());
+
+       /** The project inserter. */
+       private ProjectInserter projectInserter;
+
+       /** The “copy URI” action. */
+       private Action copyURIAction;
+
+       /** The “request URI” textfield. */
+       private JTextField requestURITextField;
+
+       /** The “start time” label. */
+       private JLabel startTimeLabel;
+
+       /** The progress bar. */
+       private JProgressBar progressBar;
+
+       /** The start time of the insert. */
+       private long startTime = 0;
+
+       /** The number of inserted blocks. */
+       private volatile int insertedBlocks;
+
+       /** Whether the “copy URI to clipboard” button was used. */
+       private boolean uriCopied;
+
+       /** Whether the insert is currently running. */
+       private volatile boolean running = false;
+
+       /**
+        * Creates a new progress insert wizard page.
+        *
+        * @param wizard
+        *            The wizard this page belongs to
+        */
+       public ProjectInsertPage(final TWizard wizard) {
+               super(wizard);
+               createActions();
+               pageInit();
+               setHeading(I18n.getMessage("jsite.insert.heading"));
+               setDescription(I18n.getMessage("jsite.insert.description"));
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               setHeading(I18n.getMessage("jsite.insert.heading"));
+                               setDescription(I18n.getMessage("jsite.insert.description"));
+                       }
+               });
+               projectInserter = new ProjectInserter();
+               projectInserter.addInsertListener(this);
+       }
+
+       /**
+        * Creates all used actions.
+        */
+       private void createActions() {
+               copyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionCopyURI();
+                       }
+               };
+               copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
+               copyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U);
+               copyURIAction.setEnabled(false);
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               copyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri"));
+                               copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
+                       }
+               });
+       }
+
+       /**
+        * Initializes the page.
+        */
+       private void pageInit() {
+               setLayout(new BorderLayout(12, 12));
+               add(createProjectInsertPanel(), BorderLayout.CENTER);
+       }
+
+       /**
+        * Creates the main panel.
+        *
+        * @return The main panel
+        */
+       private JComponent createProjectInsertPanel() {
+               JComponent projectInsertPanel = new JPanel(new GridBagLayout());
+
+               requestURITextField = new JTextField();
+               requestURITextField.setEditable(false);
+
+               startTimeLabel = new JLabel();
+
+               progressBar = new JProgressBar(0, 1);
+               progressBar.setStringPainted(true);
+               progressBar.setValue(0);
+
+               final JLabel projectInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.insert.project-information") + "</b></html>");
+               projectInsertPanel.add(projectInformationLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
+               final JLabel requestURILabel = new JLabel(I18n.getMessage("jsite.insert.request-uri") + ":");
+               projectInsertPanel.add(requestURILabel, 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));
+               final JLabel startTimeLeftLabel = new JLabel(I18n.getMessage("jsite.insert.start-time") + ":");
+               projectInsertPanel.add(startTimeLeftLabel, 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));
+               final JLabel progressLabel = new JLabel(I18n.getMessage("jsite.insert.progress") + ":");
+               projectInsertPanel.add(progressLabel, 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));
+               projectInsertPanel.add(new JButton(copyURIAction), new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               projectInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.insert.project-information") + "</b></html>");
+                               requestURILabel.setText(I18n.getMessage("jsite.insert.request-uri") + ":");
+                               startTimeLeftLabel.setText(I18n.getMessage("jsite.insert.start-time") + ":");
+                               if (startTime != 0) {
+                                       startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date(startTime)));
+                               } else {
+                                       startTimeLabel.setText("");
+                               }
+                               progressLabel.setText(I18n.getMessage("jsite.insert.progress") + ":");
+                       }
+               });
+
+               return projectInsertPanel;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+               this.wizard.setPreviousEnabled(false);
+               this.wizard.setNextName(I18n.getMessage("jsite.general.cancel"));
+               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+       }
+
+       /**
+        * Starts the insert.
+        */
+       public void startInsert() {
+               running = true;
+               copyURIAction.setEnabled(false);
+               progressBar.setValue(0);
+               progressBar.setString(I18n.getMessage("jsite.insert.starting"));
+               progressBar.setFont(progressBar.getFont().deriveFont(Font.PLAIN));
+               projectInserter.start(new ProgressListener() {
+
+                       public void onProgress(final long copied, final long length) {
+                               SwingUtilities.invokeLater(new Runnable() {
+
+                                       /**
+                                        * {@inheritDoc}
+                                        */
+                                       @SuppressWarnings("synthetic-access")
+                                       public void run() {
+                                               int divisor = 1;
+                                               while (((copied / divisor) > Integer.MAX_VALUE) || ((length / divisor) > Integer.MAX_VALUE)) {
+                                                       divisor *= 10;
+                                               }
+                                               progressBar.setMaximum((int) (length / divisor));
+                                               progressBar.setValue((int) (copied / divisor));
+                                               progressBar.setString("Uploaded: " + copied + " / " + length);
+                                       }
+                               });
+                       }
+               });
+       }
+
+       /**
+        * Stops the currently running insert.
+        */
+       public void stopInsert() {
+               if (running) {
+                       wizard.setNextEnabled(false);
+                       projectInserter.stop();
+               }
+       }
+
+       /**
+        * Returns whether the insert is currently running.
+        *
+        * @return {@code true} if the insert is currently running, {@code false}
+        *         otherwise
+        */
+       public boolean isRunning() {
+               return running;
+       }
+
+       /**
+        * Sets the project to insert.
+        *
+        * @param project
+        *            The project to insert
+        */
+       public void setProject(final Project project) {
+               projectInserter.setProject(project);
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               requestURITextField.setText(project.getFinalRequestURI(1));
+                       }
+               });
+       }
+
+       /**
+        * Sets the freenet interface to use.
+        *
+        * @param freenetInterface
+        *            The freenet interface to use
+        */
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               projectInserter.setFreenetInterface(freenetInterface);
+       }
+
+       /**
+        * Sets the project inserter’s temp directory.
+        *
+        * @see ProjectInserter#setTempDirectory(String)
+        * @param tempDirectory
+        *            The temp directory to use, or {@code null} to use the system
+        *            default
+        */
+       public void setTempDirectory(String tempDirectory) {
+               projectInserter.setTempDirectory(tempDirectory);
+       }
+
+       /**
+        * Returns whether the “copy URI to clipboard” button was used.
+        *
+        * @return {@code true} if an URI was copied to clipboard, {@code false}
+        *         otherwise
+        */
+       public boolean wasUriCopied() {
+               return uriCopied;
+       }
+
+       /**
+        * Sets whether to use the “early encode“ flag for the insert.
+        *
+        * @param useEarlyEncode
+        *            {@code true} to set the “early encode” flag for the insert,
+        *            {@code false} otherwise
+        */
+       public void setUseEarlyEncode(boolean useEarlyEncode) {
+               projectInserter.setUseEarlyEncode(useEarlyEncode);
+       }
+
+       /**
+        * Sets the insert priority.
+        *
+        * @param priority
+        *            The insert priority
+        */
+       public void setPriority(PriorityClass priority) {
+               projectInserter.setPriority(priority);
+       }
+
+       /**
+        * Sets the manifest putter to use for the insert.
+        *
+        * @see ProjectInserter#setManifestPutter(ManifestPutter)
+        * @param manifestPutter
+        *            The manifest putter
+        */
+       public void setManifestPutter(ManifestPutter manifestPutter) {
+               projectInserter.setManifestPutter(manifestPutter);
+       }
+
+       //
+       // INTERFACE InsertListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertStarted(final Project project) {
+
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date()));
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectUploadFinished(Project project) {
+               startTime = System.currentTimeMillis();
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               progressBar.setString(I18n.getMessage("jsite.insert.starting"));
+                               progressBar.setValue(0);
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectURIGenerated(Project project, final String uri) {
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               copyURIAction.setEnabled(true);
+                               requestURITextField.setText(uri);
+                       }
+               });
+               logger.log(Level.FINEST, "Insert generated URI: " + uri);
+               int slash = uri.indexOf('/');
+               slash = uri.indexOf('/', slash + 1);
+               int secondSlash = uri.indexOf('/', slash + 1);
+               if (secondSlash == -1) {
+                       secondSlash = uri.length();
+               }
+               String editionNumber = uri.substring(slash + 1, secondSlash);
+               logger.log(Level.FINEST, "Extracted edition number: " + editionNumber);
+               int edition = -1;
+               try {
+                       edition = Integer.valueOf(editionNumber);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               logger.log(Level.FINEST, "Insert edition: " + edition + ", Project edition: " + project.getEdition());
+               if ((edition != -1) && (edition == project.getEdition())) {
+                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.reinserted-edition"), I18n.getMessage("jsite.insert.reinserted-edition.title"), JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertProgress(Project project, final int succeeded, final int failed, final int fatal, final int total, final boolean finalized) {
+               insertedBlocks = succeeded;
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               if (total == 0) {
+                                       return;
+                               }
+                               progressBar.setMaximum(total);
+                               progressBar.setValue(succeeded + failed + fatal);
+                               int progress = (succeeded + failed + fatal) * 100 / total;
+                               StringBuilder progressString = new StringBuilder();
+                               progressString.append(progress).append("% (");
+                               progressString.append(succeeded + failed + fatal).append('/').append(total);
+                               progressString.append(") (");
+                               progressString.append(getTransferRate());
+                               progressString.append(' ').append(I18n.getMessage("jsite.insert.k-per-s")).append(')');
+                               progressBar.setString(progressString.toString());
+                               if (finalized) {
+                                       progressBar.setFont(progressBar.getFont().deriveFont(Font.BOLD));
+                               }
+                       }
+               });
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertFinished(Project project, boolean success, Throwable cause) {
+               running = false;
+               if (success) {
+                       String copyURILabel = I18n.getMessage("jsite.insert.okay-copy-uri");
+                       int selectedValue = JOptionPane.showOptionDialog(this, I18n.getMessage("jsite.insert.inserted"), I18n.getMessage("jsite.insert.done.title"), 0, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { I18n.getMessage("jsite.general.ok"), copyURILabel }, copyURILabel);
+                       if (selectedValue == 1) {
+                               actionCopyURI();
+                       }
+               } else {
+                       if (cause == null) {
+                               JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-failed"), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE);
+                       } else {
+                               if (cause instanceof AbortedException) {
+                                       JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-aborted"), I18n.getMessage("jsite.insert.insert-aborted.title"), JOptionPane.INFORMATION_MESSAGE);
+                               } else {
+                                       JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.insert.insert-failed-with-cause"), cause.getMessage()), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE);
+                               }
+                       }
+               }
+               SwingUtilities.invokeLater(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               progressBar.setValue(progressBar.getMaximum());
+                               progressBar.setString(I18n.getMessage("jsite.insert.done") + " (" + getTransferRate() + " " + I18n.getMessage("jsite.insert.k-per-s") + ")");
+                               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+                               wizard.setNextEnabled(true);
+                               wizard.setQuitEnabled(true);
+                       }
+               });
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Copies the request URI of the project to the clipboard.
+        */
+       private void actionCopyURI() {
+               uriCopied = true;
+               Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+               clipboard.setContents(new StringSelection(requestURITextField.getText()), this);
+       }
+
+       /**
+        * Formats the given number so that it always has the the given number of
+        * fractional digits.
+        *
+        * @param number
+        *            The number to format
+        * @param digits
+        *            The number of fractional digits
+        * @return The formatted number
+        */
+       private String formatNumber(double number, int digits) {
+               int multiplier = (int) Math.pow(10, digits);
+               String formattedNumber = String.valueOf((int) (number * multiplier) / (double) multiplier);
+               if (formattedNumber.indexOf('.') == -1) {
+                       formattedNumber += '.';
+                       for (int digit = 0; digit < digits; digit++) {
+                               formattedNumber += "0";
+                       }
+               }
+               return formattedNumber;
+       }
+
+       /**
+        * Returns the formatted transfer rate at this point.
+        *
+        * @return The formatted transfer rate
+        */
+       private String getTransferRate() {
+               return formatNumber(insertedBlocks * 32.0 / ((System.currentTimeMillis() - startTime) / 1000), 1);
+       }
+
+       //
+       // INTERFACE ClipboardOwner
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void lostOwnership(Clipboard clipboard, Transferable contents) {
+               /* ignore. */
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectPage.java
new file mode 100644 (file)
index 0000000..98ad847
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * jSite - ProjectPage.java - Copyright © 2006–2012 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.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.ClipboardOwner;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+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.JTextField;
+import javax.swing.ListSelectionModel;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
+
+import de.todesbaum.jsite.application.Freenet7Interface;
+import de.todesbaum.jsite.application.KeyDialog;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.i18n.I18n;
+import de.todesbaum.jsite.i18n.I18nContainer;
+import de.todesbaum.util.swing.SortedListModel;
+import de.todesbaum.util.swing.TLabel;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+
+/**
+ * Wizard page that lets the user manage his projects and start inserts.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ProjectPage extends TWizardPage implements ListSelectionListener, DocumentListener, ClipboardOwner {
+
+       /** The freenet interface. */
+       private Freenet7Interface freenetInterface;
+
+       /** The “browse” action. */
+       private Action projectLocalPathBrowseAction;
+
+       /** The “add project” action. */
+       private Action projectAddAction;
+
+       /** The “delete project” action. */
+       private Action projectDeleteAction;
+
+       /** The “clone project” action. */
+       private Action projectCloneAction;
+
+       /** The “manage keys” action. */
+       private Action projectManageKeysAction;
+
+       /** The “copy URI” action. */
+       private Action projectCopyURIAction;
+
+       /** The “reset edition” action. */
+       private Action projectResetEditionAction;
+
+       /** The file chooser. */
+       private JFileChooser pathChooser;
+
+       /** The project list model. */
+       private SortedListModel<Project> projectListModel;
+
+       /** The project list scroll pane. */
+       private JScrollPane projectScrollPane;
+
+       /** The project list. */
+       private JList projectList;
+
+       /** The project name textfield. */
+       private JTextField projectNameTextField;
+
+       /** The project description textfield. */
+       private JTextField projectDescriptionTextField;
+
+       /** The local path textfield. */
+       private JTextField projectLocalPathTextField;
+
+       /** The textfield for the complete URI. */
+       private JTextField projectCompleteUriTextField;
+
+       /** The project path textfield. */
+       private JTextField projectPathTextField;
+
+       /** Whether the “copy URI to clipboard” action was used. */
+       private boolean uriCopied;
+
+       /**
+        * Creates a new project page.
+        *
+        * @param wizard
+        *            The wizard this page belongs to
+        */
+       public ProjectPage(final TWizard wizard) {
+               super(wizard);
+               setLayout(new BorderLayout(12, 12));
+               dialogInit();
+               setHeading(I18n.getMessage("jsite.project.heading"));
+               setDescription(I18n.getMessage("jsite.project.description"));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               setHeading(I18n.getMessage("jsite.project.heading"));
+                               setDescription(I18n.getMessage("jsite.project.description"));
+                       }
+               });
+       }
+
+       /**
+        * Initializes the page.
+        */
+       private void dialogInit() {
+               createActions();
+
+               pathChooser = new JFileChooser();
+               projectListModel = new SortedListModel<Project>();
+               projectList = new JList(projectListModel);
+               projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               projectList.addListSelectionListener(this);
+
+               add(projectScrollPane = new JScrollPane(projectList), BorderLayout.LINE_START);
+               projectScrollPane.setPreferredSize(new Dimension(150, projectList.getPreferredSize().height));
+               add(createInformationPanel(), BorderLayout.CENTER);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void pageAdded(TWizard wizard) {
+               super.pageAdded(wizard);
+               projectList.clearSelection();
+               this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes"));
+               this.wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+               this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit"));
+               this.wizard.setNextEnabled(false);
+       }
+
+       /**
+        * Adds the given listener to the list of listeners.
+        *
+        * @param listener
+        *            The listener to add
+        */
+       public void addListSelectionListener(ListSelectionListener listener) {
+               projectList.addListSelectionListener(listener);
+       }
+
+       /**
+        * Removes the given listener from the list of listeners.
+        *
+        * @param listener
+        *            The listener to remove
+        */
+       public void removeListSelectionListener(ListSelectionListener listener) {
+               projectList.removeListSelectionListener(listener);
+       }
+
+       /**
+        * Creates all actions.
+        */
+       private void createActions() {
+               projectLocalPathBrowseAction = new AbstractAction(I18n.getMessage("jsite.project.action.browse")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       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")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       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")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       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")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       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);
+
+               projectCopyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionCopyURI();
+                       }
+               };
+               projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
+               projectCopyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U);
+               projectCopyURIAction.setEnabled(false);
+
+               projectManageKeysAction = new AbstractAction(I18n.getMessage("jsite.project.action.manage-keys")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionManageKeys();
+                       }
+               };
+               projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip"));
+               projectManageKeysAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_M);
+               projectManageKeysAction.setEnabled(false);
+
+               projectResetEditionAction = new AbstractAction(I18n.getMessage("jsite.project.action.reset-edition")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               actionResetEdition();
+                       }
+               };
+               projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip"));
+               projectResetEditionAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R);
+               projectResetEditionAction.setEnabled(false);
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               projectLocalPathBrowseAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.browse"));
+                               projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip"));
+                               projectAddAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.add-project"));
+                               projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip"));
+                               projectDeleteAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.delete-project"));
+                               projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip"));
+                               projectCloneAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.clone-project"));
+                               projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip"));
+                               projectCopyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri"));
+                               projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip"));
+                               projectManageKeysAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.manage-keys"));
+                               projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip"));
+                               projectResetEditionAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.reset-edition"));
+                               projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip"));
+                               pathChooser.setApproveButtonText(I18n.getMessage("jsite.project.action.browse.choose"));
+                       }
+               });
+       }
+
+       /**
+        * Creates the information panel.
+        *
+        * @return The information panel
+        */
+       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));
+               functionButtons.add(new JButton(projectManageKeysAction));
+
+               informationPanel.add(functionButtons, BorderLayout.PAGE_START);
+               informationPanel.add(informationTable, BorderLayout.CENTER);
+
+               final JLabel projectInformationLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.information") + "</b></html>");
+               informationTable.add(projectInformationLabel, 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);
+
+               final TLabel projectNameLabel = new TLabel(I18n.getMessage("jsite.project.project.name") + ":", KeyEvent.VK_N, projectNameTextField);
+               informationTable.add(projectNameLabel, 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);
+
+               final TLabel projectDescriptionLabel = new TLabel(I18n.getMessage("jsite.project.project.description") + ":", KeyEvent.VK_D, projectDescriptionTextField);
+               informationTable.add(projectDescriptionLabel, 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);
+
+               final TLabel projectLocalPathLabel = new TLabel(I18n.getMessage("jsite.project.project.local-path") + ":", KeyEvent.VK_L, projectLocalPathTextField);
+               informationTable.add(projectLocalPathLabel, 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));
+
+               final JLabel projectAddressLabel = new JLabel("<html><b>" + I18n.getMessage("jsite.project.project.address") + "</b></html>");
+               informationTable.add(projectAddressLabel, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0));
+
+               projectPathTextField = new JTextField();
+               projectPathTextField.getDocument().putProperty("name", "project.path");
+               projectPathTextField.getDocument().addDocumentListener(this);
+               ((AbstractDocument) projectPathTextField.getDocument()).setDocumentFilter(new DocumentFilter() {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
+                               super.insertString(fb, offset, string.replaceAll("/", ""), attr);
+                               updateCompleteURI();
+                       }
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
+                               super.replace(fb, offset, length, text.replaceAll("/", ""), attrs);
+                               updateCompleteURI();
+                       }
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
+                               super.remove(fb, offset, length);
+                               updateCompleteURI();
+                       }
+               });
+               projectPathTextField.setEnabled(false);
+
+               final TLabel projectPathLabel = new TLabel(I18n.getMessage("jsite.project.project.path") + ":", KeyEvent.VK_P, projectPathTextField);
+               informationTable.add(projectPathLabel, 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(projectPathTextField, new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               projectCompleteUriTextField = new JTextField();
+               projectCompleteUriTextField.setEditable(false);
+               final TLabel projectUriLabel = new TLabel(I18n.getMessage("jsite.project.project.uri") + ":", KeyEvent.VK_U, projectCompleteUriTextField);
+               informationTable.add(projectUriLabel, 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(projectCompleteUriTextField, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+               informationTable.add(new JButton(projectCopyURIAction), new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0));
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       public void run() {
+                               projectInformationLabel.setText("<html><b>" + I18n.getMessage("jsite.project.project.information") + "</b></html>");
+                               projectNameLabel.setText(I18n.getMessage("jsite.project.project.name") + ":");
+                               projectDescriptionLabel.setText(I18n.getMessage("jsite.project.project.description") + ":");
+                               projectLocalPathLabel.setText(I18n.getMessage("jsite.project.project.local-path") + ":");
+                               projectAddressLabel.setText("<html><b>" + I18n.getMessage("jsite.project.project.address") + "</b></html>");
+                               projectPathLabel.setText(I18n.getMessage("jsite.project.project.path") + ":");
+                               projectUriLabel.setText(I18n.getMessage("jsite.project.project.uri") + ":");
+                       }
+               });
+
+               return informationPanel;
+       }
+
+       /**
+        * Sets the project list.
+        *
+        * @param projects
+        *            The list of projects
+        */
+       public void setProjects(Project[] projects) {
+               projectListModel.clear();
+               for (Project project : projects) {
+                       projectListModel.add(project);
+               }
+       }
+
+       /**
+        * Returns the list of projects.
+        *
+        * @return The list of projects
+        */
+       public Project[] getProjects() {
+               return projectListModel.toArray(new Project[projectListModel.size()]);
+       }
+
+       /**
+        * Sets the freenet interface to use.
+        *
+        * @param freenetInterface
+        *            The freenetInterface to use
+        */
+       public void setFreenetInterface(Freenet7Interface freenetInterface) {
+               this.freenetInterface = freenetInterface;
+       }
+
+       /**
+        * Returns the currently selected project.
+        *
+        * @return The currently selected project
+        */
+       public Project getSelectedProject() {
+               return (Project) projectList.getSelectedValue();
+       }
+
+       /**
+        * Returns whether the “copy URI to clipboard” button was used.
+        *
+        * @return {@code true} if the “copy URI to clipboard” button was used,
+        *         {@code false} otherwise
+        */
+       public boolean wasUriCopied() {
+               return uriCopied;
+       }
+
+       /**
+        * Updates the currently selected project with changed information from a
+        * textfield.
+        *
+        * @param documentEvent
+        *            The document event to process
+        */
+       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) {
+                       /* ignore. */
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Lets the user choose a local path for a project.
+        */
+       private 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());
+               }
+       }
+
+       /**
+        * Adds a new project.
+        */
+       private 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;
+               }
+               Project newProject = new Project();
+               newProject.setName(I18n.getMessage("jsite.project.new-project.name"));
+               newProject.setInsertURI(keyPair[0]);
+               newProject.setRequestURI(keyPair[1]);
+               newProject.setEdition(-1);
+               newProject.setPath("");
+               projectListModel.add(newProject);
+               projectScrollPane.revalidate();
+               projectScrollPane.repaint();
+               projectList.setSelectedIndex(projectListModel.indexOf(newProject));
+       }
+
+       /**
+        * Deletes the currently selected project.
+        */
+       private 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));
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Clones the currently selected project.
+        */
+       private void actionClone() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project newProject = new Project((Project) projectList.getSelectedValue());
+                       newProject.setName(MessageFormat.format(I18n.getMessage("jsite.project.action.clone-project.copy"), newProject.getName()));
+                       projectListModel.add(newProject);
+                       projectList.setSelectedIndex(projectListModel.indexOf(newProject));
+               }
+       }
+
+       /**
+        * Copies the request URI of the currently selected project to the
+        * clipboard.
+        */
+       private void actionCopyURI() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project selectedProject = (Project) projectList.getSelectedValue();
+                       Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+                       clipboard.setContents(new StringSelection(selectedProject.getFinalRequestURI(0)), this);
+                       uriCopied = true;
+               }
+       }
+
+       /**
+        * Opens a {@link KeyDialog} and lets the user manipulate the keys of the
+        * project.
+        */
+       private void actionManageKeys() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project selectedProject = (Project) projectList.getSelectedValue();
+                       KeyDialog keyDialog = new KeyDialog(freenetInterface, wizard);
+                       keyDialog.setPrivateKey(selectedProject.getInsertURI());
+                       keyDialog.setPublicKey(selectedProject.getRequestURI());
+                       keyDialog.setVisible(true);
+                       if (!keyDialog.wasCancelled()) {
+                               String originalPublicKey = selectedProject.getRequestURI();
+                               String originalPrivateKey = selectedProject.getInsertURI();
+                               selectedProject.setInsertURI(keyDialog.getPrivateKey());
+                               selectedProject.setRequestURI(keyDialog.getPublicKey());
+                               if (!originalPublicKey.equals(selectedProject.getRequestURI()) || !originalPrivateKey.equals(selectedProject.getInsertURI())) {
+                                       selectedProject.setEdition(-1);
+                               }
+                               updateCompleteURI();
+                       }
+               }
+       }
+
+       /**
+        * Resets the edition of the currently selected project.
+        */
+       private void actionResetEdition() {
+               if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.reset-edition"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) {
+                       return;
+               }
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project selectedProject = (Project) projectList.getSelectedValue();
+                       selectedProject.setEdition(-1);
+                       updateCompleteURI();
+               }
+       }
+
+       /**
+        * Updates the complete URI text field.
+        */
+       private void updateCompleteURI() {
+               int selectedIndex = projectList.getSelectedIndex();
+               if (selectedIndex > -1) {
+                       Project selectedProject = (Project) projectList.getSelectedValue();
+                       projectCompleteUriTextField.setText(selectedProject.getFinalRequestURI(0));
+               }
+       }
+
+       //
+       // 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);
+               projectPathTextField.setEnabled(selectedRow > -1);
+               projectLocalPathBrowseAction.setEnabled(selectedRow > -1);
+               projectDeleteAction.setEnabled(selectedRow > -1);
+               projectCloneAction.setEnabled(selectedRow > -1);
+               projectCopyURIAction.setEnabled(selectedRow > -1);
+               projectManageKeysAction.setEnabled(selectedRow > -1);
+               projectResetEditionAction.setEnabled(selectedRow > -1);
+               if (selectedRow > -1) {
+                       projectNameTextField.setText(selectedProject.getName());
+                       projectDescriptionTextField.setText(selectedProject.getDescription());
+                       projectLocalPathTextField.setText(selectedProject.getLocalPath());
+                       projectPathTextField.setText(selectedProject.getPath());
+                       projectCompleteUriTextField.setText("freenet:" + selectedProject.getFinalRequestURI(0));
+               } else {
+                       projectNameTextField.setText("");
+                       projectDescriptionTextField.setText("");
+                       projectLocalPathTextField.setText("");
+                       projectPathTextField.setText("");
+                       projectCompleteUriTextField.setText("");
+               }
+       }
+
+       //
+       // INTERFACE ChangeListener
+       //
+
+       //
+       // 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);
+       }
+
+       //
+       // INTERFACE ClipboardOwner
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void lostOwnership(Clipboard clipboard, Transferable contents) {
+               /* ignore. */
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/i18n/I18n.java b/src/main/java/de/todesbaum/jsite/i18n/I18n.java
new file mode 100644 (file)
index 0000000..4c1cceb
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * jSite - I18n.java - Copyright © 2006–2012 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.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Maps i18n keys to translated texts, depending on a current locale.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class I18n {
+
+       /** The default locale, English. */
+       private static Locale defaultLocale = new Locale("en");
+
+       /** The current locale. */
+       private static Locale currentLocale;
+
+       /**
+        * Returns the currently set locale.
+        *
+        * @return The current locale
+        */
+       public static Locale getLocale() {
+               if (currentLocale == null) {
+                       currentLocale = Locale.getDefault();
+               }
+               return currentLocale;
+       }
+
+       /**
+        * Sets the current locale.
+        *
+        * @param locale
+        *            The new current locale
+        */
+       public static void setLocale(Locale locale) {
+               currentLocale = locale;
+               Locale.setDefault(locale);
+       }
+
+       /**
+        * Returns the resource bundle for the current locale.
+        *
+        * @return The resource bundle for the current locale
+        */
+       public static ResourceBundle getResourceBundle() {
+               return getResourceBundle(getLocale());
+       }
+
+       /**
+        * Returns the resource bundle for the given locale.
+        *
+        * @param locale
+        *            The locale to get the resource bundle for
+        * @return The resource bundle for the given locale
+        */
+       public static ResourceBundle getResourceBundle(Locale locale) {
+               return ResourceBundle.getBundle("de.todesbaum.jsite.i18n.jSite", locale);
+       }
+
+       /**
+        * Retrieves a translated text for the given i18n key. If the resource
+        * bundle for the current locale does not have a translation for the given
+        * key, the default locale is tried. If that fails, the key is returned.
+        *
+        * @param key
+        *            The key to get the translation for
+        * @return The translated value, or the key itself if not translation can be
+        *         found
+        */
+       public static String getMessage(String key) {
+               try {
+                       return getResourceBundle().getString(key);
+               } catch (MissingResourceException mre1) {
+                       try {
+                               return getResourceBundle(defaultLocale).getString(key);
+                       } catch (MissingResourceException mre2) {
+                               return key;
+                       }
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java b/src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java
new file mode 100644 (file)
index 0000000..da2e0c3
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * jSite - I18nContainer.java - Copyright © 2007–2012 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.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container that collects {@link Runnable}s that change the texts of GUI
+ * components when the current locale has changed.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class I18nContainer implements Iterable<Runnable> {
+
+       /** The container singleton. */
+       private static final I18nContainer singleton = new I18nContainer();
+
+       /** The list of runnables that change texts. */
+       private final List<Runnable> i18nRunnables = Collections.synchronizedList(new ArrayList<Runnable>());
+
+       /**
+        * The list of runnables that change texts and run after
+        * {@link #i18nRunnables}.
+        */
+       private final List<Runnable> i18nPostRunnables = Collections.synchronizedList(new ArrayList<Runnable>());
+
+       /**
+        * Returns the singleton instance.
+        *
+        * @return The singleton instance
+        */
+       public static I18nContainer getInstance() {
+               return singleton;
+       }
+
+       /**
+        * Registers an i18n runnable that is run when the current locale has
+        * changed.
+        *
+        * @param i18nRunnable
+        *            The runnable to register
+        */
+       public void registerRunnable(Runnable i18nRunnable) {
+               i18nRunnables.add(i18nRunnable);
+       }
+
+       /**
+        * Registers a {@link Runnable} that changes texts when the current locale
+        * has changed and runs after {@link #i18nRunnables} have run.
+        *
+        * @param i18nPostRunnable
+        *            The runnable to register
+        */
+       public void registerPostRunnable(Runnable i18nPostRunnable) {
+               i18nPostRunnables.add(i18nPostRunnable);
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * Returns a combined list of {@link #i18nRunnables} and
+        * {@link #i18nPostRunnables}, in that order.
+        */
+       public Iterator<Runnable> iterator() {
+               List<Runnable> allRunnables = new ArrayList<Runnable>();
+               allRunnables.addAll(i18nRunnables);
+               allRunnables.addAll(i18nPostRunnables);
+               return allRunnables.iterator();
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/main/CLI.java b/src/main/java/de/todesbaum/jsite/main/CLI.java
new file mode 100644 (file)
index 0000000..65c9253
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * jSite - CLI.java - Copyright © 2006–2012 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.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;
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
+
+/**
+ * Command-line interface for jSite.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class CLI implements InsertListener {
+
+       /** Object used for synchronization. */
+       private Object lockObject = new Object();
+
+       /** Writer for the console. */
+       private PrintWriter outputWriter = new PrintWriter(System.out, true);
+
+       /** The freenet interface. */
+       private Freenet7Interface freenetInterface;
+
+       /** The project inserter. */
+       private ProjectInserter projectInserter = new ProjectInserter();
+
+       /** The list of nodes. */
+       private Node[] nodes;
+
+       /** The projects. */
+       private Project[] projects;
+
+       /** Whether the insert has finished. */
+       private boolean finished = false;
+
+       /** Whether the insert finished successfully. */
+       private boolean success;
+
+       /**
+        * Creates a new command-line interface.
+        *
+        * @param args
+        *            The command-line arguments
+        */
+       private CLI(String[] args) {
+
+               if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) {
+                       outputWriter.println("\nParameters:\n");
+                       outputWriter.println("  --config-file=<configuration file>");
+                       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;
+               }
+
+               String configFile = System.getProperty("user.home") + "/.jSite/config7";
+               for (String argument : args) {
+                       String value = argument.substring(argument.indexOf('=') + 1).trim();
+                       if (argument.startsWith("--config-file=")) {
+                               configFile = value;
+                       }
+               }
+
+               ConfigurationLocator configurationLocator = new ConfigurationLocator();
+               if (configFile != null) {
+                       configurationLocator.setCustomLocation(configFile);
+               }
+               Configuration configuration = new Configuration(configurationLocator, configurationLocator.findPreferredLocation());
+
+               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 (String argument : args) {
+                       if (argument.startsWith("--config-file=")) {
+                               /* we already parsed this one. */
+                               continue;
+                       }
+                       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;
+                               }
+                               currentProject.setEdition(Integer.parseInt(value));
+                       } else {
+                               outputWriter.println("Unknown parameter: " + argument);
+                               return;
+                       }
+               }
+
+               int errorCode = 1;
+               if (currentProject != null) {
+                       if (insertProject(currentProject)) {
+                               outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted.");
+                               errorCode = 0;
+                       } else {
+                               outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted.");
+                       }
+               }
+
+               configuration.setProjects(projects);
+               configuration.save();
+
+               System.exit(errorCode);
+       }
+
+       /**
+        * Returns the project with the given name.
+        *
+        * @param name
+        *            The name of the project
+        * @return The project, or <code>null</code> if no project could be found
+        */
+       private Project getProject(String name) {
+               for (Project project : projects) {
+                       if (project.getName().equals(name)) {
+                               return project;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the node with the given name.
+        *
+        * @param name
+        *            The name of the node
+        * @return The node, or <code>null</code> if no node could be found
+        */
+       private Node getNode(String name) {
+               for (Node node : nodes) {
+                       if (node.getName().equals(name)) {
+                               return node;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Inserts the given project.
+        *
+        * @param currentProject
+        *            The project to insert
+        * @return <code>true</code> if the insert finished successfully,
+        *         <code>false</code> otherwise
+        */
+       private boolean insertProject(Project currentProject) {
+               if (!freenetInterface.hasNode()) {
+                       outputWriter.println("Node is not running!");
+                       return false;
+               }
+               projectInserter.setProject(currentProject);
+               projectInserter.start(new ProgressListener() {
+
+                       public void onProgress(long copied, long length) {
+                               System.out.print("Uploaded: " + copied + " / " + length + " bytes...\r");
+                       }
+               });
+               synchronized (lockObject) {
+                       while (!finished) {
+                               try {
+                                       lockObject.wait();
+                               } catch (InterruptedException e) {
+                                       /* ignore, we're in a loop. */
+                               }
+                       }
+               }
+               return success;
+       }
+
+       //
+       // INTERFACE InsertListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectInsertStarted(Project project) {
+               outputWriter.println("Starting Insert of project \"" + project.getName() + "\".");
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectUploadFinished(Project project) {
+               outputWriter.println("Project \"" + project.getName() + "\" has been uploaded, starting insert...");
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void projectURIGenerated(Project project, String uri) {
+               outputWriter.println("URI: " + uri);
+       }
+
+       /**
+        * {@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.getFinalRequestURI(0));
+               finished = true;
+               this.success = success;
+               synchronized (lockObject) {
+                       lockObject.notify();
+               }
+       }
+
+       //
+       // MAIN
+       //
+
+       /**
+        * Creates a new command-line interface with the given arguments.
+        *
+        * @param args
+        *            The command-line arguments
+        */
+       public static void main(String[] args) {
+               new CLI(args);
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/main/Configuration.java b/src/main/java/de/todesbaum/jsite/main/Configuration.java
new file mode 100644 (file)
index 0000000..f5001b0
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * jSite - Configuration.java - Copyright © 2006–2012 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 java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.todesbaum.jsite.application.FileOption;
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
+import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
+import de.todesbaum.util.freenet.fcp2.PriorityClass;
+import de.todesbaum.util.io.Closer;
+import de.todesbaum.util.io.StreamCopier;
+import de.todesbaum.util.xml.SimpleXML;
+import de.todesbaum.util.xml.XML;
+
+/**
+ * The configuration.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Configuration {
+
+       /** The root node of the configuration. */
+       private SimpleXML rootNode;
+
+       /** The configuration locator. */
+       private final ConfigurationLocator configurationLocator;
+
+       /** Where the configuration resides. */
+       private ConfigurationLocation configurationLocation;
+
+       /**
+        * Creates a new configuration that is read from the given file.
+        *
+        * @param configurationLocator
+        *            The configuration locator
+        * @param configurationLocation
+        *            The configuration directory
+        */
+       public Configuration(ConfigurationLocator configurationLocator, ConfigurationLocation configurationLocation) {
+               this.configurationLocator = configurationLocator;
+               this.configurationLocation = configurationLocation;
+               readConfiguration(configurationLocator.getFile(configurationLocation));
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the configuration locator.
+        *
+        * @return The configuration locator
+        */
+       public ConfigurationLocator getConfigurationLocator() {
+               return configurationLocator;
+       }
+
+       /**
+        * Returns the location the configuration will be written to when calling
+        * {@link #save()}.
+        *
+        * @return The location the configuration will be written to
+        */
+       public ConfigurationLocation getConfigurationDirectory() {
+               return configurationLocation;
+       }
+
+       /**
+        * Sets the location the configuration will be written to when calling
+        * {@link #save()}.
+        *
+        * @param configurationLocation
+        *            The location to write the configuration to
+        */
+       public void setConfigurationLocation(ConfigurationLocation configurationLocation) {
+               this.configurationLocation = configurationLocation;
+       }
+
+       /**
+        * Reads the configuration from the file.
+        *
+        * @param filename
+        *            The name of the file to read the configuration from
+        */
+       private void readConfiguration(String filename) {
+               Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Reading configuration from: " + filename);
+               if (filename != null) {
+                       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) {
+                                       /* ignore. */
+                               } catch (IOException e) {
+                                       /* ignore. */
+                               } finally {
+                                       Closer.close(fileInputStream);
+                                       Closer.close(fileByteOutputStream);
+                               }
+                       }
+               }
+               rootNode = new SimpleXML("configuration");
+       }
+
+       /**
+        * Saves the configuration.
+        *
+        * @return <code>true</code> if the configuration could be saved,
+        *         <code>false</code> otherwise
+        */
+       public boolean save() {
+               Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Trying to save configuration to: " + configurationLocation);
+               File configurationFile = new File(configurationLocator.getFile(configurationLocation));
+               if (!configurationFile.exists()) {
+                       File configurationFilePath = configurationFile.getAbsoluteFile().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) {
+                       /* ignore. */
+               } finally {
+                       Closer.close(configurationInputStream);
+                       Closer.close(fileOutputStream);
+               }
+               return false;
+       }
+
+       /**
+        * Returns the value of a node.
+        *
+        * @param nodeNames
+        *            The name of all nodes in the chain
+        * @param defaultValue
+        *            The default value to return if the node could not be found
+        * @return The value of the node, or the default value if the node could not
+        *         be found
+        */
+       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();
+       }
+
+       /**
+        * Returns the integer value of a node.
+        *
+        * @param nodeNames
+        *            The names of all nodes in the chain
+        * @param defaultValue
+        *            The default value to return if the node can not be found
+        * @return The parsed integer value, or the default value if the node can
+        *         not be found or the value can not be parsed into an integer
+        */
+       private int getNodeIntValue(String[] nodeNames, int defaultValue) {
+               try {
+                       return Integer.parseInt(getNodeValue(nodeNames, String.valueOf(defaultValue)));
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return defaultValue;
+       }
+
+       /**
+        * Returns the boolean value of a node.
+        *
+        * @param nodeNames
+        *            The names of all nodes in the chain
+        * @param defaultValue
+        *            The default value to return if the node can not be found
+        * @return The parsed boolean value, or the default value if the node can
+        *         not be found
+        */
+       private boolean getNodeBooleanValue(String[] nodeNames, boolean defaultValue) {
+               String nodeValue = getNodeValue(nodeNames, null);
+               if (nodeValue == null) {
+                       return defaultValue;
+               }
+               return Boolean.parseBoolean(nodeValue);
+       }
+
+       /**
+        * Returns the hostname of the node.
+        *
+        * @return The hostname of the node
+        * @deprecated Use {@link #getSelectedNode()} instead
+        */
+       @Deprecated
+       public String getNodeAddress() {
+               return getNodeValue(new String[] { "node-address" }, "localhost");
+       }
+
+       /**
+        * Sets the hostname of the node.
+        *
+        * @param nodeAddress
+        *            The hostname of the node
+        * @deprecated Use {@link #setSelectedNode(Node)} instead
+        */
+       @Deprecated
+       public void setNodeAddress(String nodeAddress) {
+               rootNode.replace("node-address", nodeAddress);
+       }
+
+       /**
+        * The port number of the node
+        *
+        * @return The port number of the node
+        * @deprecated Use {@link #getSelectedNode()} instead.
+        */
+       @Deprecated
+       public int getNodePort() {
+               return getNodeIntValue(new String[] { "node-port" }, 9481);
+       }
+
+       /**
+        * Sets the port number of the node.
+        *
+        * @param nodePort
+        *            The port number of the node
+        * @deprecated Use {@link #setSelectedNode(Node)} instead
+        */
+       @Deprecated
+       public void setNodePort(int nodePort) {
+               rootNode.replace("node-port", String.valueOf(nodePort));
+       }
+
+       /**
+        * Returns whether the node configuration page should be skipped on startup.
+        *
+        * @return <code>true</code> to skip the node configuration page on startup,
+        *         <code>false</code> to show it
+        */
+       public boolean isSkipNodePage() {
+               return getNodeBooleanValue(new String[] { "skip-node-page" }, false);
+       }
+
+       /**
+        * Sets whether the node configuration page should be skipped on startup.
+        *
+        * @param skipNodePage
+        *            <code>true</code> to skip the node configuration page on
+        *            startup, <code>false</code> to show it
+        */
+       public void setSkipNodePage(boolean skipNodePage) {
+               rootNode.replace("skip-node-page", String.valueOf(skipNodePage));
+       }
+
+       /**
+        * Returns all configured projects.
+        *
+        * @return A list of all projects
+        */
+       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 = new Project();
+                                       projects.add(project);
+                                       project.setDescription(projectNode.getNode("description").getValue(""));
+                                       String indexFile = projectNode.getNode("index-file").getValue("");
+                                       if (indexFile.indexOf('/') > -1) {
+                                               indexFile = "";
+                                       }
+                                       project.setIndexFile(indexFile);
+                                       project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue("0")));
+                                       project.setLocalPath(projectNode.getNode("local-path").getValue(""));
+                                       project.setName(projectNode.getNode("name").getValue(""));
+                                       project.setPath(projectNode.getNode("path").getValue(""));
+                                       if ((project.getPath() != null) && (project.getPath().indexOf("/") != -1)) {
+                                               project.setPath(project.getPath().replaceAll("/", ""));
+                                       }
+                                       project.setEdition(Integer.parseInt(projectNode.getNode("edition").getValue("0")));
+                                       project.setInsertURI(projectNode.getNode("insert-uri").getValue(""));
+                                       project.setRequestURI(projectNode.getNode("request-uri").getValue(""));
+                                       if (projectNode.getNode("ignore-hidden-files") != null) {
+                                               project.setIgnoreHiddenFiles(Boolean.parseBoolean(projectNode.getNode("ignore-hidden-files").getValue("true")));
+                                       } else {
+                                               project.setIgnoreHiddenFiles(true);
+                                       }
+
+                                       /* load last insert hashes. */
+                                       Map<String, FileOption> fileOptions = new HashMap<String, FileOption>();
+                                       SimpleXML lastInsertHashesNode = projectNode.getNode("last-insert-hashes");
+                                       if (lastInsertHashesNode != null) {
+                                               for (SimpleXML fileNode : lastInsertHashesNode.getNodes("file")) {
+                                                       String filename = fileNode.getNode("filename").getValue();
+                                                       String lastInsertHash = fileNode.getNode("last-insert-hash").getValue();
+                                                       int lastInsertEdition = Integer.valueOf(fileNode.getNode("last-insert-edition").getValue());
+                                                       String lastInsertFilename = filename;
+                                                       if (fileNode.getNode("last-insert-filename") != null) {
+                                                               lastInsertFilename = fileNode.getNode("last-insert-filename").getValue();
+                                                       }
+                                                       FileOption fileOption = project.getFileOption(filename);
+                                                       fileOption.setLastInsertHash(lastInsertHash).setLastInsertEdition(lastInsertEdition).setLastInsertFilename(lastInsertFilename);
+                                                       fileOptions.put(filename, fileOption);
+                                               }
+                                       }
+
+                                       SimpleXML fileOptionsNode = projectNode.getNode("file-options");
+                                       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()));
+                                                       if (fileOptionNode.getNode("insert-redirect") != null) {
+                                                               fileOption.setInsertRedirect(Boolean.parseBoolean(fileOptionNode.getNode("insert-redirect").getValue()));
+                                                       }
+                                                       fileOption.setCustomKey(fileOptionNode.getNode("custom-key").getValue(""));
+                                                       if (fileOptionNode.getNode("changed-name") != null) {
+                                                               fileOption.setChangedName(fileOptionNode.getNode("changed-name").getValue());
+                                                       }
+                                                       fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue(""));
+                                                       fileOptions.put(filename, fileOption);
+                                               }
+                                       }
+                                       project.setFileOptions(fileOptions);
+                               } catch (NumberFormatException nfe1) {
+                                       nfe1.printStackTrace();
+                               }
+                       }
+               }
+               return projects.toArray(new Project[projects.size()]);
+       }
+
+       /**
+        * Sets the list of all projects.
+        *
+        * @param projects
+        *            The list of all projects
+        */
+       public void setProjects(Project[] projects) {
+               SimpleXML projectsNode = new SimpleXML("project-list");
+               for (Project project : projects) {
+                       SimpleXML projectNode = projectsNode.append("project");
+                       projectNode.append("edition", String.valueOf(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());
+                       projectNode.append("ignore-hidden-files", String.valueOf(project.isIgnoreHiddenFiles()));
+
+                       /* store last insert hashes. */
+                       SimpleXML lastInsertHashesNode = projectNode.append("last-insert-hashes");
+                       for (Entry<String, FileOption> fileOption : project.getFileOptions().entrySet()) {
+                               if ((fileOption.getValue().getLastInsertHash() == null) || (fileOption.getValue().getLastInsertHash().length() == 0)) {
+                                       continue;
+                               }
+                               SimpleXML fileNode = lastInsertHashesNode.append("file");
+                               fileNode.append("filename", fileOption.getKey());
+                               fileNode.append("last-insert-hash", fileOption.getValue().getLastInsertHash());
+                               fileNode.append("last-insert-edition", String.valueOf(fileOption.getValue().getLastInsertEdition()));
+                               fileNode.append("last-insert-filename", fileOption.getValue().getLastInsertFilename());
+                       }
+
+                       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("insert-redirect", String.valueOf(fileOption.isInsertRedirect()));
+                                       fileOptionNode.append("custom-key", fileOption.getCustomKey());
+                                       fileOptionNode.append("changed-name", fileOption.getChangedName());
+                                       fileOptionNode.append("mime-type", fileOption.getMimeType());
+                               }
+                       }
+               }
+               rootNode.replace(projectsNode);
+       }
+
+       /**
+        * Returns the stored locale.
+        *
+        * @return The stored locale
+        */
+       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);
+       }
+
+       /**
+        * Sets the locale to store.
+        *
+        * @param locale
+        *            The locale to store
+        */
+       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;
+       }
+
+       /**
+        * Returns a list of configured nodes.
+        *
+        * @return The list of the configured nodes
+        */
+       public Node[] getNodes() {
+               SimpleXML nodesNode = rootNode.getNode("nodes");
+               if (nodesNode == null) {
+                       String hostname = getNodeAddress();
+                       int port = getNodePort();
+                       if (hostname == null) {
+                               hostname = "127.0.0.1";
+                               port = 9481;
+                       }
+                       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;
+       }
+
+       /**
+        * Sets the list of configured nodes.
+        *
+        * @param nodes
+        *            The list of configured nodes
+        */
+       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");
+       }
+
+       /**
+        * Sets the selected node.
+        *
+        * @param selectedNode
+        *            The selected node
+        */
+       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);
+       }
+
+       /**
+        * Returns the selected node.
+        *
+        * @return The selected node
+        */
+       public Node getSelectedNode() {
+               SimpleXML selectedNodeNode = rootNode.getNode("selected-node");
+               if (selectedNodeNode == null) {
+                       String hostname = getNodeAddress();
+                       int port = getNodePort();
+                       if (hostname == null) {
+                               hostname = "127.0.0.1";
+                               port = 9481;
+                       }
+                       return new Node(hostname, port, "Node");
+               }
+               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);
+       }
+
+       /**
+        * Returns the temp directory to use.
+        *
+        * @return The temp directoy, or {@code null} to use the default temp
+        *         directory
+        */
+       public String getTempDirectory() {
+               return getNodeValue(new String[] { "temp-directory" }, null);
+       }
+
+       /**
+        * Sets the temp directory to use.
+        *
+        * @param tempDirectory
+        *            The temp directory to use, or {@code null} to use the default
+        *            temp directory
+        */
+       public void setTempDirectory(String tempDirectory) {
+               if (tempDirectory != null) {
+                       SimpleXML tempDirectoryNode = new SimpleXML("temp-directory");
+                       tempDirectoryNode.setValue(tempDirectory);
+                       rootNode.replace(tempDirectoryNode);
+               } else {
+                       rootNode.remove("temp-directory");
+               }
+       }
+
+       /**
+        * Returns whether to use the “early encode“ flag for the insert.
+        *
+        * @return {@code true} to set the “early encode” flag for the insert,
+        *         {@code false} otherwise
+        */
+       public boolean useEarlyEncode() {
+               return getNodeBooleanValue(new String[] { "use-early-encode" }, false);
+       }
+
+       /**
+        * Sets whether to use the “early encode“ flag for the insert.
+        *
+        * @param useEarlyEncode
+        *            {@code true} to set the “early encode” flag for the insert,
+        *            {@code false} otherwise
+        * @return This configuration
+        */
+       public Configuration setUseEarlyEncode(boolean useEarlyEncode) {
+               rootNode.replace("use-early-encode", String.valueOf(useEarlyEncode));
+               return this;
+       }
+
+       /**
+        * Returns the insert priority.
+        *
+        * @return The insert priority
+        */
+       public PriorityClass getPriority() {
+               return PriorityClass.valueOf(getNodeValue(new String[] { "insert-priority" }, "interactive"));
+       }
+
+       /**
+        * Sets the insert priority.
+        *
+        * @param priority
+        *            The insert priority
+        * @return This configuration
+        */
+       public Configuration setPriority(PriorityClass priority) {
+               rootNode.replace("insert-priority", priority.toString());
+               return this;
+       }
+
+       /**
+        * Returns the manifest putter.
+        *
+        * @return The manifest putter
+        */
+       public ManifestPutter getManifestPutter() {
+               return ManifestPutter.valueOf(getNodeValue(new String[] { "manifest-putter" }, "simple").toUpperCase());
+       }
+
+       /**
+        * Sets the manifest putter.
+        *
+        * @param manifestPutter
+        *            The manifest putter
+        * @return This configuration
+        */
+       public Configuration setManifestPutter(ManifestPutter manifestPutter) {
+               rootNode.replace("manifest-putter", manifestPutter.name().toLowerCase());
+               return this;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/main/ConfigurationLocator.java b/src/main/java/de/todesbaum/jsite/main/ConfigurationLocator.java
new file mode 100644 (file)
index 0000000..967f936
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * jSite - ConfigurationLocator.java - Copyright © 2011–2012 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.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Locator for configuration files in different places.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ConfigurationLocator {
+
+       /**
+        * The location of the configuration directory.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       public enum ConfigurationLocation {
+
+               /** The configuration is in the same directory as the JAR file. */
+               NEXT_TO_JAR_FILE,
+
+               /**
+                * The configuration is in the user’s home directory. This is the
+                * pre-0.9.3 default.
+                */
+               HOME_DIRECTORY,
+
+               /** Custom location. */
+               CUSTOM,
+
+       }
+
+       /** The possible configuration locations. */
+       private final Map<ConfigurationLocation, String> configurationFiles = new HashMap<ConfigurationLocation, String>();
+
+       /**
+        * Creates a new configuration locator. If this class is loaded from a JAR
+        * file, {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is added to the list
+        * of possible configuration file locations.
+        * {@link ConfigurationLocation#HOME_DIRECTORY} is always added to this
+        * list, {@link ConfigurationLocation#CUSTOM} has to be enabled by calling
+        * {@link #setCustomLocation(String)}.
+        */
+       public ConfigurationLocator() {
+               /* are we executed from a JAR file? */
+               String resource = getClass().getResource("/" + getClass().getName().replace(".", "/") + ".class").toString();
+               if (resource.startsWith("jar:")) {
+                       String jarFileLocation = resource.substring(9, resource.indexOf(".jar!") + 4);
+                       String jarFileDirectory = new File(jarFileLocation).getParent();
+                       File configurationFile = new File(jarFileDirectory, "jSite.conf");
+                       configurationFiles.put(ConfigurationLocation.NEXT_TO_JAR_FILE, configurationFile.getPath());
+               }
+               File homeDirectoryFile = new File(System.getProperty("user.home"), ".jSite/config7");
+               configurationFiles.put(ConfigurationLocation.HOME_DIRECTORY, homeDirectoryFile.getPath());
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Sets the location of the custom configuration file.
+        *
+        * @param customFile
+        *            The custom location of the configuration file
+        */
+       public void setCustomLocation(String customFile) {
+               configurationFiles.put(ConfigurationLocation.CUSTOM, customFile);
+       }
+
+       /**
+        * Returns whether the given location is valid. Certain locations (such as
+        * {@link ConfigurationLocation#NEXT_TO_JAR_FILE}) may be invalid in certain
+        * circumstances (such as the application not being run from a JAR file). A
+        * location being valid does not imply that a configuration file does exist
+        * at the given location, use {@link #hasFile(ConfigurationLocation)} to
+        * check for a configuration file at the desired location.
+        *
+        * @param configurationLocation
+        *            The configuration location
+        * @return {@code true} if the location is valid, {@code false} otherwise
+        */
+       public boolean isValidLocation(ConfigurationLocation configurationLocation) {
+               return configurationFiles.containsKey(configurationLocation);
+       }
+
+       /**
+        * Checks whether a configuration file exists at the given location.
+        *
+        * @param configurationLocation
+        *            The configuration location
+        * @return {@code true} if a configuration file exists at the given
+        *         location, {@code false} otherwise
+        */
+       public boolean hasFile(ConfigurationLocation configurationLocation) {
+               if (!isValidLocation(configurationLocation)) {
+                       return false;
+               }
+               return new File(configurationFiles.get(configurationLocation)).exists();
+       }
+
+       /**
+        * Returns the configuration file for the given location.
+        *
+        * @param configurationLocation
+        *            The location to get the file for
+        * @return The name of the configuration file at the given location, or
+        *         {@code null} if the given location is invalid
+        */
+       public String getFile(ConfigurationLocation configurationLocation) {
+               return configurationFiles.get(configurationLocation);
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Finds the preferred location of the configuration file.
+        *
+        * @see #findPreferredLocation(ConfigurationLocation)
+        * @return The preferred location of the configuration file
+        */
+       public ConfigurationLocation findPreferredLocation() {
+               return findPreferredLocation(ConfigurationLocation.NEXT_TO_JAR_FILE);
+       }
+
+       /**
+        * Finds the preferred location of the configuration file. The following
+        * checks are performed: if a custom configuration location has been defined
+        * (by calling {@link #setCustomLocation(String)})
+        * {@link ConfigurationLocation#CUSTOM} is returned. If the application is
+        * run from a JAR file and a configuration file is found next to the JAR
+        * file (i.e. in the same directory),
+        * {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is returned. If a
+        * configuration file exists in the user’s home directory,
+        * {@link ConfigurationLocation#HOME_DIRECTORY} is returned. Otherwise, the
+        * given {@code defaultLocation} is returned.
+        *
+        * @param defaultLocation
+        *            The default location to return if no other configuration file
+        *            is found
+        * @return The configuration location to load the configuration from
+        */
+       public ConfigurationLocation findPreferredLocation(ConfigurationLocation defaultLocation) {
+               if (hasFile(ConfigurationLocation.CUSTOM)) {
+                       return ConfigurationLocation.CUSTOM;
+               }
+               if (hasFile(ConfigurationLocation.NEXT_TO_JAR_FILE)) {
+                       return ConfigurationLocation.NEXT_TO_JAR_FILE;
+               }
+               if (hasFile(ConfigurationLocation.HOME_DIRECTORY)) {
+                       return ConfigurationLocation.HOME_DIRECTORY;
+               }
+               if (isValidLocation(defaultLocation)) {
+                       return defaultLocation;
+               }
+               return ConfigurationLocation.HOME_DIRECTORY;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/main/Main.java b/src/main/java/de/todesbaum/jsite/main/Main.java
new file mode 100644 (file)
index 0000000..edbf5e4
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * jSite - Main.java - Copyright © 2006–2012 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.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+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.JMenuItem;
+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.Freenet7Interface;
+import de.todesbaum.jsite.application.Node;
+import de.todesbaum.jsite.application.Project;
+import de.todesbaum.jsite.application.ProjectInserter;
+import de.todesbaum.jsite.application.ProjectInserter.CheckReport;
+import de.todesbaum.jsite.application.ProjectInserter.Issue;
+import de.todesbaum.jsite.application.UpdateChecker;
+import de.todesbaum.jsite.application.UpdateListener;
+import de.todesbaum.jsite.gui.NodeManagerListener;
+import de.todesbaum.jsite.gui.NodeManagerPage;
+import de.todesbaum.jsite.gui.PreferencesPage;
+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.jsite.i18n.I18nContainer;
+import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation;
+import de.todesbaum.util.image.IconLoader;
+import de.todesbaum.util.swing.TWizard;
+import de.todesbaum.util.swing.TWizardPage;
+import de.todesbaum.util.swing.WizardListener;
+
+/**
+ * The main class that ties together everything.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Main implements ActionListener, ListSelectionListener, WizardListener, NodeManagerListener, UpdateListener {
+
+       /** The logger. */
+       private static final Logger logger = Logger.getLogger(Main.class.getName());
+
+       /** The version. */
+       private static final Version VERSION = new Version(0, 10);
+
+       /** The configuration. */
+       private Configuration configuration;
+
+       /** The freenet interface. */
+       private Freenet7Interface freenetInterface = new Freenet7Interface();
+
+       /** The update checker. */
+       private final UpdateChecker updateChecker;
+
+       /** The jSite icon. */
+       private Icon jSiteIcon;
+
+       /**
+        * Enumeration for all possible pages.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       private static enum PageType {
+
+               /** The node manager page. */
+               PAGE_NODE_MANAGER,
+
+               /** The project page. */
+               PAGE_PROJECTS,
+
+               /** The project files page. */
+               PAGE_PROJECT_FILES,
+
+               /** The project insert page. */
+               PAGE_INSERT_PROJECT,
+
+               /** The preferences page. */
+               PAGE_PREFERENCES
+
+       }
+
+       /** The supported locales. */
+       private static final Locale[] SUPPORTED_LOCALES = new Locale[] { Locale.ENGLISH, Locale.GERMAN, Locale.FRENCH };
+
+       /** The actions that switch the language. */
+       private Map<Locale, Action> languageActions = new HashMap<Locale, Action>();
+
+       /** The “manage nodes” action. */
+       private Action manageNodeAction;
+
+       /** The “preferences” action. */
+       private Action optionsPreferencesAction;
+
+       /** The “check for updates” action. */
+       private Action checkForUpdatesAction;
+
+       /** The “about jSite” action. */
+       private Action aboutAction;
+
+       /** The wizard. */
+       private TWizard wizard;
+
+       /** The node menu. */
+       private JMenu nodeMenu;
+
+       /** The currently selected node. */
+       private Node selectedNode;
+
+       /** Mapping from page type to page. */
+       private final Map<PageType, TWizardPage> pages = new HashMap<PageType, TWizardPage>();
+
+       /** The original location of the configuration file. */
+       private ConfigurationLocation originalLocation;
+
+       /**
+        * Creates a new core with the default configuration file.
+        */
+       private Main() {
+               this(null);
+       }
+
+       /**
+        * Creates a new core with the given configuration from the given file.
+        *
+        * @param configFilename
+        *            The name of the configuration file
+        */
+       private Main(String configFilename) {
+               /* collect all possible configuration file locations. */
+               ConfigurationLocator configurationLocator = new ConfigurationLocator();
+               if (configFilename != null) {
+                       configurationLocator.setCustomLocation(configFilename);
+               }
+
+               originalLocation = configurationLocator.findPreferredLocation();
+               logger.log(Level.CONFIG, "Using configuration from " + originalLocation + ".");
+               configuration = new Configuration(configurationLocator, originalLocation);
+
+               Locale.setDefault(configuration.getLocale());
+               I18n.setLocale(configuration.getLocale());
+               wizard = new TWizard();
+               createActions();
+               wizard.setJMenuBar(createMenuBar());
+               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);
+
+               updateChecker = new UpdateChecker(freenetInterface);
+               updateChecker.addUpdateListener(this);
+               updateChecker.start();
+
+               initPages();
+               showPage(PageType.PAGE_PROJECTS);
+       }
+
+       /**
+        * Creates all actions.
+        */
+       private void createActions() {
+               for (final Locale locale : SUPPORTED_LOCALES) {
+                       languageActions.put(locale, new AbstractAction(I18n.getMessage("jsite.menu.language." + locale.getLanguage()), IconLoader.loadIcon("/flag-" + locale.getLanguage() + ".png")) {
+
+                               @SuppressWarnings("synthetic-access")
+                               public void actionPerformed(ActionEvent actionEvent) {
+                                       switchLanguage(locale);
+                               }
+                       });
+               }
+               manageNodeAction = new AbstractAction(I18n.getMessage("jsite.menu.nodes.manage-nodes")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               showPage(PageType.PAGE_NODE_MANAGER);
+                               optionsPreferencesAction.setEnabled(true);
+                               wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
+                               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+                       }
+               };
+               optionsPreferencesAction = new AbstractAction(I18n.getMessage("jsite.menu.options.preferences")) {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               optionsPreferences();
+                       }
+               };
+               checkForUpdatesAction = new AbstractAction(I18n.getMessage("jsite.menu.help.check-for-updates")) {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               showLatestUpdate();
+                       }
+               };
+               aboutAction = new AbstractAction(I18n.getMessage("jsite.menu.help.about")) {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent e) {
+                               JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.about.message"), getVersion().toString()), null, JOptionPane.INFORMATION_MESSAGE, jSiteIcon);
+                       }
+               };
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               manageNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.nodes.manage-nodes"));
+                               optionsPreferencesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.options.preferences"));
+                               checkForUpdatesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.check-for-updates"));
+                               aboutAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.about"));
+                       }
+               });
+       }
+
+       /**
+        * Creates the menu bar.
+        *
+        * @return The menu bar
+        */
+       private JMenuBar createMenuBar() {
+               JMenuBar menuBar = new JMenuBar();
+               final 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());
+
+               final JMenu optionsMenu = new JMenu(I18n.getMessage("jsite.menu.options"));
+               menuBar.add(optionsMenu);
+               optionsMenu.add(optionsPreferencesAction);
+
+               /* evil hack to right-align the help menu */
+               JPanel panel = new JPanel();
+               panel.setOpaque(false);
+               menuBar.add(panel);
+
+               final JMenu helpMenu = new JMenu(I18n.getMessage("jsite.menu.help"));
+               menuBar.add(helpMenu);
+               helpMenu.add(checkForUpdatesAction);
+               helpMenu.add(aboutAction);
+
+               I18nContainer.getInstance().registerRunnable(new Runnable() {
+
+                       @SuppressWarnings("synthetic-access")
+                       public void run() {
+                               languageMenu.setText(I18n.getMessage("jsite.menu.languages"));
+                               nodeMenu.setText(I18n.getMessage("jsite.menu.nodes"));
+                               optionsMenu.setText(I18n.getMessage("jsite.menu.options"));
+                               helpMenu.setText(I18n.getMessage("jsite.menu.help"));
+                               for (Map.Entry<Locale, Action> languageActionEntry : languageActions.entrySet()) {
+                                       languageActionEntry.getValue().putValue(Action.NAME, I18n.getMessage("jsite.menu.language." + languageActionEntry.getKey().getLanguage()));
+                               }
+                       }
+               });
+
+               return menuBar;
+       }
+
+       /**
+        * Initializes all pages.
+        */
+       private void initPages() {
+               NodeManagerPage nodeManagerPage = new NodeManagerPage(wizard);
+               nodeManagerPage.setName("page.node-manager");
+               nodeManagerPage.addNodeManagerListener(this);
+               nodeManagerPage.setNodes(configuration.getNodes());
+               pages.put(PageType.PAGE_NODE_MANAGER, nodeManagerPage);
+
+               ProjectPage projectPage = new ProjectPage(wizard);
+               projectPage.setName("page.project");
+               projectPage.setProjects(configuration.getProjects());
+               projectPage.setFreenetInterface(freenetInterface);
+               projectPage.addListSelectionListener(this);
+               pages.put(PageType.PAGE_PROJECTS, projectPage);
+
+               ProjectFilesPage projectFilesPage = new ProjectFilesPage(wizard);
+               projectFilesPage.setName("page.project.files");
+               pages.put(PageType.PAGE_PROJECT_FILES, projectFilesPage);
+
+               ProjectInsertPage projectInsertPage = new ProjectInsertPage(wizard);
+               projectInsertPage.setName("page.project.insert");
+               projectInsertPage.setFreenetInterface(freenetInterface);
+               pages.put(PageType.PAGE_INSERT_PROJECT, projectInsertPage);
+
+               PreferencesPage preferencesPage = new PreferencesPage(wizard);
+               preferencesPage.setName("page.preferences");
+               preferencesPage.setTempDirectory(configuration.getTempDirectory());
+               pages.put(PageType.PAGE_PREFERENCES, preferencesPage);
+       }
+
+       /**
+        * Shows the page with the given type.
+        *
+        * @param pageType
+        *            The page type to show
+        */
+       private 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");
+       }
+
+       /**
+        * Returns whether a configuration file would be overwritten when calling
+        * {@link #saveConfiguration()}.
+        *
+        * @return {@code true} if {@link #saveConfiguration()} would overwrite an
+        *         existing file, {@code false} otherwise
+        */
+       private boolean isOverwritingConfiguration() {
+               return configuration.getConfigurationLocator().hasFile(configuration.getConfigurationDirectory());
+       }
+
+       /**
+        * Saves the configuration.
+        *
+        * @return <code>true</code> if the configuration could be saved,
+        *         <code>false</code> otherwise
+        */
+       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());
+
+               PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES);
+               configuration.setTempDirectory(preferencesPage.getTempDirectory());
+
+               return configuration.save();
+       }
+
+       /**
+        * Finds a supported locale for the given locale.
+        *
+        * @param forLocale
+        *            The locale to find a supported locale for
+        * @return The supported locale that was found, or the default locale if no
+        *         supported locale could be found
+        */
+       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];
+       }
+
+       /**
+        * Returns the version.
+        *
+        * @return The version
+        */
+       public static final Version getVersion() {
+               return VERSION;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Switches the language of the interface to the given locale.
+        *
+        * @param locale
+        *            The locale to switch to
+        */
+       private void switchLanguage(Locale locale) {
+               Locale supportedLocale = findSupportedLocale(locale);
+               Action languageAction = languageActions.get(supportedLocale);
+               JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) languageAction.getValue("menuItem");
+               menuItem.setSelected(true);
+               I18n.setLocale(supportedLocale);
+               for (Runnable i18nRunnable : I18nContainer.getInstance()) {
+                       try {
+                               i18nRunnable.run();
+                       } catch (Throwable t) {
+                               /* we probably shouldn't swallow this. */
+                       }
+               }
+               wizard.setPage(wizard.getPage());
+               configuration.setLocale(supportedLocale);
+       }
+
+       /**
+        * Shows a dialog with general preferences.
+        */
+       private void optionsPreferences() {
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setConfigurationLocation(configuration.getConfigurationDirectory());
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasNextToJarConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.NEXT_TO_JAR_FILE));
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasCustomConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.CUSTOM));
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setUseEarlyEncode(configuration.useEarlyEncode());
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setPriority(configuration.getPriority());
+               ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setManifestPutter(configuration.getManifestPutter());
+               showPage(PageType.PAGE_PREFERENCES);
+               optionsPreferencesAction.setEnabled(false);
+               wizard.setNextEnabled(true);
+               wizard.setNextName(I18n.getMessage("jsite.wizard.next"));
+       }
+
+       /**
+        * Shows a dialog box that shows the last version that was found by the
+        * {@link UpdateChecker}.
+        */
+       private void showLatestUpdate() {
+               Version latestVersion = updateChecker.getLatestVersion();
+               int versionDifference = latestVersion.compareTo(VERSION);
+               if (versionDifference > 0) {
+                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.newer.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
+               } else if (versionDifference < 0) {
+                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.older.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
+               } else {
+                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.okay.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
+
+       //
+       // 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);
+               } 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.warning.no-local-path"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.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);
+               } 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.error.no-node-selected"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       CheckReport checkReport = ProjectInserter.validateProject(project);
+                       for (Issue issue : checkReport) {
+                               if (issue.isFatal()) {
+                                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.ERROR_MESSAGE);
+                                       return;
+                               }
+                               if (JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) {
+                                       return;
+                               }
+                       }
+                       boolean nodeRunning = false;
+                       try {
+                               nodeRunning = freenetInterface.isNodePresent();
+                       } catch (IOException e) {
+                               /* ignore. */
+                       }
+                       if (!nodeRunning) {
+                               JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-running"), null, JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       configuration.save();
+                       showPage(PageType.PAGE_INSERT_PROJECT);
+                       ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT);
+                       String tempDirectory = ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).getTempDirectory();
+                       projectInsertPage.setTempDirectory(tempDirectory);
+                       projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode());
+                       projectInsertPage.setPriority(configuration.getPriority());
+                       projectInsertPage.setManifestPutter(configuration.getManifestPutter());
+                       projectInsertPage.startInsert();
+                       nodeMenu.setEnabled(false);
+                       optionsPreferencesAction.setEnabled(false);
+               } else if ("page.project.insert".equals(pageName)) {
+                       ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT);
+                       if (projectInsertPage.isRunning()) {
+                               projectInsertPage.stopInsert();
+                       } else {
+                               showPage(PageType.PAGE_PROJECTS);
+                               nodeMenu.setEnabled(true);
+                               optionsPreferencesAction.setEnabled(true);
+                       }
+               } else if ("page.preferences".equals(pageName)) {
+                       PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES);
+                       showPage(PageType.PAGE_PROJECTS);
+                       optionsPreferencesAction.setEnabled(true);
+                       configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode());
+                       configuration.setPriority(preferencesPage.getPriority());
+                       configuration.setManifestPutter(preferencesPage.getManifestPutter());
+                       configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation());
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void wizardPreviousPressed(TWizard wizard) {
+               String pageName = wizard.getPage().getName();
+               if ("page.project".equals(pageName) || "page.preferences".equals(pageName)) {
+                       showPage(PageType.PAGE_NODE_MANAGER);
+                       optionsPreferencesAction.setEnabled(true);
+               } else if ("page.project.files".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECTS);
+               } else if ("page.project.insert".equals(pageName)) {
+                       showPage(PageType.PAGE_PROJECT_FILES);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void wizardQuitPressed(TWizard wizard) {
+               if (((ProjectPage) pages.get(PageType.PAGE_PROJECTS)).wasUriCopied() || ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).wasUriCopied()) {
+                       JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.use-clipboard-now"));
+               }
+               if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), I18n.getMessage("jsite.quit.question.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
+                       if (isOverwritingConfiguration() && !originalLocation.equals(configuration.getConfigurationDirectory())) {
+                               int overwriteConfigurationAnswer = JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.quit.overwrite-configuration"), configuration.getConfigurationLocator().getFile(configuration.getConfigurationDirectory())), I18n.getMessage("jsite.quit.overwrite-configuration.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
+                               if (overwriteConfigurationAnswer == JOptionPane.YES_OPTION) {
+                                       if (saveConfiguration()) {
+                                               System.exit(0);
+                                       }
+                               } else if (overwriteConfigurationAnswer == JOptionPane.CANCEL_OPTION) {
+                                       return;
+                               }
+                               if (overwriteConfigurationAnswer == JOptionPane.NO_OPTION) {
+                                       System.exit(0);
+                               }
+                       } else {
+                               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 nodeSelected(Node node) {
+               for (Component menuItem : nodeMenu.getMenuComponents()) {
+                       if (menuItem instanceof JMenuItem) {
+                               if (node.equals(((JMenuItem) menuItem).getClientProperty("Node"))) {
+                                       ((JMenuItem) menuItem).setSelected(true);
+                               }
+                       }
+               }
+               freenetInterface.setNode(node);
+               selectedNode = node;
+       }
+
+       //
+       // INTERFACE ActionListener
+       //
+
+       /**
+        * {@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);
+               }
+       }
+
+       //
+       // INTERFACE UpdateListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void foundUpdateData(Version foundVersion, long versionTimestamp) {
+               logger.log(Level.FINEST, "Found version {0} from {1,date}.", new Object[] { foundVersion, versionTimestamp });
+               if (foundVersion.compareTo(VERSION) > 0) {
+                       JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.found-version.message"), foundVersion.toString(), new Date(versionTimestamp)), I18n.getMessage("jsite.update-checker.found-version.title"), JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
+
+       //
+       // MAIN METHOD
+       //
+
+       /**
+        * Main method that is called by the VM.
+        *
+        * @param args
+        *            The command-line arguments
+        */
+       public static void main(String[] args) {
+               /* initialize logger. */
+               Logger logger = Logger.getLogger("de.todesbaum");
+               Handler handler = new ConsoleHandler();
+               logger.addHandler(handler);
+               String configFilename = null;
+               boolean nextIsConfigFilename = false;
+               for (String argument : args) {
+                       if (nextIsConfigFilename) {
+                               configFilename = argument;
+                               nextIsConfigFilename = false;
+                       }
+                       if ("--help".equals(argument)) {
+                               printHelp();
+                               return;
+                       } else if ("--debug".equals(argument)) {
+                               logger.setLevel(Level.ALL);
+                               handler.setLevel(Level.ALL);
+                       } else if ("--config-file".equals(argument)) {
+                               nextIsConfigFilename = true;
+                       }
+               }
+               if (nextIsConfigFilename) {
+                       System.out.println("--config-file needs parameter!");
+                       return;
+               }
+               new Main(configFilename);
+       }
+
+       /**
+        * Prints a small syntax help.
+        */
+       private static void printHelp() {
+               System.out.println("--help\tshows this cruft");
+               System.out.println("--debug\tenables some debug output");
+               System.out.println("--config-file <file>\tuse specified configuration file");
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/jsite/main/Version.java b/src/main/java/de/todesbaum/jsite/main/Version.java
new file mode 100644 (file)
index 0000000..977836c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * jSite - Version.java - Copyright © 2006–2012 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;
+
+/**
+ * Container for version information.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class Version implements Comparable<Version> {
+
+       /** The components of the version information. */
+       private final int[] components;
+
+       /**
+        * Creates a new version container with the given components.
+        *
+        * @param components
+        *            The version components
+        */
+       public Version(int... components) {
+               this.components = new int[components.length];
+               System.arraycopy(components, 0, this.components, 0, components.length);
+       }
+
+       /**
+        * Returns the number of version components.
+        *
+        * @return The number of version components
+        */
+       public int size() {
+               return components.length;
+       }
+
+       /**
+        * Returns the version component with the given index.
+        *
+        * @param index
+        *            The index of the version component
+        * @return The version component
+        */
+       public int getComponent(int index) {
+               return components[index];
+       }
+
+       /**
+        * Parses a version from the given string.
+        *
+        * @param versionString
+        *            The version string to parse
+        * @return The parsed version, or <code>null</code> if the string could not
+        *         be parsed
+        */
+       public static Version parse(String versionString) {
+               String[] componentStrings = versionString.split("\\.");
+               int[] components = new int[componentStrings.length];
+               int index = -1;
+               for (String componentString : componentStrings) {
+                       try {
+                               components[++index] = Integer.parseInt(componentString);
+                       } catch (NumberFormatException nfe1) {
+                               return null;
+                       }
+               }
+               return new Version(components);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String toString() {
+               StringBuilder versionString = new StringBuilder();
+               for (int component : components) {
+                       if (versionString.length() != 0) {
+                               versionString.append('.');
+                       }
+                       versionString.append(component);
+               }
+               return versionString.toString();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int compareTo(Version version) {
+               int lessComponents = Math.min(components.length, version.components.length);
+               for (int index = 0; index < lessComponents; index++) {
+                       if (version.components[index] == components[index]) {
+                               continue;
+                       }
+                       return components[index] - version.components[index];
+               }
+               return components.length - version.components.length;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Client.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Client.java
new file mode 100644 (file)
index 0000000..ede92d1
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * jSite - Client.java - Copyright © 2006–2012 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;
+
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
+
+/**
+ * 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$
+ */
+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. 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
+        * @param progressListener
+        *            The progress listener for payload transfers
+        * @throws IOException
+        *             if an I/O error occurs
+        * @see #execute(Command, boolean)
+        */
+       public void execute(Command command, ProgressListener progressListener) throws IOException {
+               execute(command, true, progressListener);
+       }
+
+       /**
+        * 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 {
+               execute(command, removeExistingIdentifiers, null);
+       }
+
+       /**
+        * 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
+        * @param progressListener
+        *            The progress listener for payload transfers
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void execute(Command command, boolean removeExistingIdentifiers, ProgressListener progressListener) throws IOException {
+               synchronized (messageQueue) {
+                       messageQueue.clear();
+                       if (removeExistingIdentifiers) {
+                               identifiers.clear();
+                       }
+                       identifiers.add(command.getIdentifier());
+               }
+               connection.execute(command, progressListener);
+       }
+
+       /**
+        * 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/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java
new file mode 100644 (file)
index 0000000..5b340e4
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * jSite - ClientGet.java - Copyright © 2008–2012 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 “ClientGet” command.
+ *
+ * @author David ‘BombeB Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class ClientGet extends Command {
+
+       private boolean ignoreDataStore;
+       private boolean dataStoreOnly;
+       private String uri;
+       private Verbosity verbosity = Verbosity.NONE;
+       private long maxSize = -1;
+       private long maxTempSize = -1;
+       private int maxRetries = -1;
+       private PriorityClass priorityClass = PriorityClass.INTERACTIVE;
+       private Persistence persistence = Persistence.CONNECTION;
+       private String clientToken;
+       private boolean global = false;
+       private ReturnType returnType = ReturnType.direct;
+       private boolean binaryBlob = false;
+       private String allowedMimeTypes = null;
+       private String filename = null;
+       private String tempFilename = null;
+
+       /**
+        *Creates a new ClientGet command with the given request identifier.
+        *
+        * @param identifier
+        *            The request identifier
+        */
+       public ClientGet(String identifier) {
+               super("ClientGet", identifier);
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public boolean isIgnoreDataStore() {
+               return ignoreDataStore;
+       }
+
+       /**
+        * TODO
+        *
+        * @param ignoreDataStore
+        */
+       public void setIgnoreDataStore(boolean ignoreDataStore) {
+               this.ignoreDataStore = ignoreDataStore;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public boolean isDataStoreOnly() {
+               return dataStoreOnly;
+       }
+
+       /**
+        * TODO
+        *
+        * @param dataStoreOnly
+        */
+       public void setDataStoreOnly(boolean dataStoreOnly) {
+               this.dataStoreOnly = dataStoreOnly;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public String getUri() {
+               return uri;
+       }
+
+       /**
+        * TODO
+        *
+        * @param uri
+        */
+       public void setUri(String uri) {
+               this.uri = uri;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public Verbosity getVerbosity() {
+               return verbosity;
+       }
+
+       /**
+        * TODO
+        *
+        * @param verbosity
+        */
+       public void setVerbosity(Verbosity verbosity) {
+               this.verbosity = verbosity;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public long getMaxSize() {
+               return maxSize;
+       }
+
+       /**
+        * TODO
+        *
+        * @param maxSize
+        */
+       public void setMaxSize(long maxSize) {
+               this.maxSize = maxSize;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public long getMaxTempSize() {
+               return maxTempSize;
+       }
+
+       /**
+        * TODO
+        *
+        * @param maxTempSize
+        */
+       public void setMaxTempSize(long maxTempSize) {
+               this.maxTempSize = maxTempSize;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public int getMaxRetries() {
+               return maxRetries;
+       }
+
+       /**
+        * TODO
+        *
+        * @param maxRetries
+        */
+       public void setMaxRetries(int maxRetries) {
+               this.maxRetries = maxRetries;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public PriorityClass getPriorityClass() {
+               return priorityClass;
+       }
+
+       /**
+        * TODO
+        *
+        * @param priorityClass
+        */
+       public void setPriorityClass(PriorityClass priorityClass) {
+               this.priorityClass = priorityClass;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public Persistence getPersistence() {
+               return persistence;
+       }
+
+       /**
+        * TODO
+        *
+        * @param persistence
+        */
+       public void setPersistence(Persistence persistence) {
+               this.persistence = persistence;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public String getClientToken() {
+               return clientToken;
+       }
+
+       /**
+        * TODO
+        *
+        * @param clientToken
+        */
+       public void setClientToken(String clientToken) {
+               this.clientToken = clientToken;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public boolean isGlobal() {
+               return global;
+       }
+
+       /**
+        * TODO
+        *
+        * @param global
+        */
+       public void setGlobal(boolean global) {
+               this.global = global;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public ReturnType getReturnType() {
+               return returnType;
+       }
+
+       /**
+        * TODO
+        *
+        * @param returnType
+        */
+       public void setReturnType(ReturnType returnType) {
+               this.returnType = returnType;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public boolean isBinaryBlob() {
+               return binaryBlob;
+       }
+
+       /**
+        * TODO
+        *
+        * @param binaryBlob
+        */
+       public void setBinaryBlob(boolean binaryBlob) {
+               this.binaryBlob = binaryBlob;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public String getAllowedMimeTypes() {
+               return allowedMimeTypes;
+       }
+
+       /**
+        * TODO
+        *
+        * @param allowedMimeTypes
+        */
+       public void setAllowedMimeTypes(String allowedMimeTypes) {
+               this.allowedMimeTypes = allowedMimeTypes;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public String getFilename() {
+               return filename;
+       }
+
+       /**
+        * TODO
+        *
+        * @param filename
+        */
+       public void setFilename(String filename) {
+               this.filename = filename;
+       }
+
+       /**
+        * TODO
+        *
+        * @return
+        */
+       public String getTempFilename() {
+               return tempFilename;
+       }
+
+       /**
+        * TODO
+        *
+        * @param tempFilename
+        */
+       public void setTempFilename(String tempFilename) {
+               this.tempFilename = tempFilename;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               super.write(writer);
+               writer.write("IgnoreDS=" + ignoreDataStore + LINEFEED);
+               writer.write("DSonly=" + dataStoreOnly + LINEFEED);
+               writer.write("URI=" + uri + LINEFEED);
+               writer.write("Verbosity=" + verbosity.getValue() + LINEFEED);
+               if (maxSize > -1) {
+                       writer.write("MaxSize=" + maxSize + LINEFEED);
+               }
+               if (maxTempSize > -1) {
+                       writer.write("MaxTempSize=" + maxTempSize + LINEFEED);
+               }
+               if (maxRetries >= -1) {
+                       writer.write("MaxRetries=" + maxRetries + LINEFEED);
+               }
+               writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED);
+               writer.write("Persistence=" + persistence.getName() + LINEFEED);
+               if (clientToken != null) {
+                       writer.write("ClientToken=" + clientToken + LINEFEED);
+               }
+               writer.write("Global=" + global + LINEFEED);
+               writer.write("BinaryBlob=" + binaryBlob + LINEFEED);
+               if (allowedMimeTypes != null) {
+                       writer.write("AllowedMIMETypes=" + allowedMimeTypes + LINEFEED);
+               }
+               if (returnType == ReturnType.disk) {
+                       writer.write("Filename=" + filename + LINEFEED);
+                       if (tempFilename != null) {
+                               writer.write("TempFilename=" + tempFilename + LINEFEED);
+                       }
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java
new file mode 100644 (file)
index 0000000..5983078
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * jSite - ClientHello.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java
new file mode 100644 (file)
index 0000000..9993173
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * jSite - ClientPut.java - Copyright © 2006–2012 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$
+ */
+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;
+
+       /** Whether to generate the keys early. */
+       protected boolean earlyEncode = false;
+
+       /** 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 whether the data should be encoded early to generate the final
+        * key as fast as possible.
+        *
+        * @return {@code true} if the key should be generated early, {@code false}
+        *         otherwise
+        */
+       public boolean isEarlyEncode() {
+               return earlyEncode;
+       }
+
+       /**
+        * Sets whether the data should be encoded early to generate the final key
+        * as fast as possible.
+        *
+        * @param earlyEncode
+        *            {@code true} if the key should be generated early, {@code
+        *            false} otherwise
+        */
+       public void setEarlyEncode(boolean earlyEncode) {
+               this.earlyEncode = earlyEncode;
+       }
+
+       /**
+        * 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);
+               writer.write("EarlyEncode=" + earlyEncode);
+               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/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java
new file mode 100644 (file)
index 0000000..d72f2c1
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * jSite - ClientPutComplexDir.java - Copyright © 2006–2012 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.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.todesbaum.util.io.Closer;
+
+/**
+ * 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$
+ */
+public class ClientPutComplexDir extends ClientPutDir<ClientPutComplexDir> {
+
+       /** 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 File payloadFile;
+
+       /** The total number of bytes of the payload. */
+       private long payloadLength = 0;
+
+       /** The temp directory to use. */
+       private final String tempDirectory;
+
+       /**
+        * 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) {
+               this(identifier, uri, null);
+       }
+
+       /**
+        * 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
+        * @param tempDirectory
+        *            The temp directory to use, or {@code null} to use the default
+        *            temp directory
+        */
+       public ClientPutComplexDir(String identifier, String uri, String tempDirectory) {
+               super("ClientPutComplexDir", identifier, uri);
+               this.tempDirectory = tempDirectory;
+       }
+
+       /**
+        * Adds a file to the directory inserted by this request.
+        *
+        * @param fileEntry
+        *            The file entry to add to the directory
+        * @throws IOException
+        *             if an I/O error occurs when creating the payload stream
+        */
+       public void addFileEntry(FileEntry fileEntry) throws IOException {
+               if (fileEntry instanceof DirectFileEntry) {
+                       if (payloadFile == null) {
+                               try {
+                                       payloadFile = File.createTempFile("payload", ".dat", (tempDirectory != null) ? new File(tempDirectory) : null);
+                                       payloadFile.deleteOnExit();
+                               } catch (IOException e) {
+                                       /* ignore. */
+                               }
+                       }
+                       if (payloadFile != null) {
+                               InputStream payloadInputStream = ((DirectFileEntry) fileEntry).getDataInputStream();
+                               FileOutputStream payloadOutputStream = null;
+                               try {
+                                       payloadOutputStream = new FileOutputStream(payloadFile, true);
+                                       byte[] buffer = new byte[65536];
+                                       int read = 0;
+                                       while ((read = payloadInputStream.read(buffer)) != -1) {
+                                               payloadOutputStream.write(buffer, 0, read);
+                                       }
+                                       payloadOutputStream.flush();
+                                       fileEntries.add(fileEntry);
+                               } catch (IOException ioe1) {
+                                       payloadFile.delete();
+                                       throw ioe1;
+                               } finally {
+                                       Closer.close(payloadOutputStream);
+                                       Closer.close(payloadInputStream);
+                               }
+                       }
+               } else {
+                       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();
+                       } 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() {
+               if (payloadFile != null) {
+                       try {
+                               return new FileInputStream(payloadFile);
+                       } catch (FileNotFoundException e) {
+                               /* shouldn't occur. */
+                       }
+               }
+               return null;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java
new file mode 100644 (file)
index 0000000..386fe1d
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * jSite - ClientPutDir.java - Copyright © 2006–2012 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.
+ *
+ * @param <C>
+ *            The type of the “ClientPutDir” command
+ * @author David Roden &lt;droden@gmail.com&gt;
+ */
+public class ClientPutDir<C extends ClientPutDir<?>> extends ClientPut {
+
+       /**
+        * All possible manifest putters. Manifest putters are used to distribute
+        * files of a directory insert to different containers, depending on size,
+        * type, and other factors.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       public enum ManifestPutter {
+
+               /**
+                * Use the “simple” manifest putter. Despite its name this is currently
+                * the default manifest putter.
+                */
+               SIMPLE("simple"),
+
+               /** Use the “default” manifest putter. */
+               DEFAULT("default");
+
+               /** The name of the manifest putter. */
+               private final String name;
+
+               /**
+                * Creates a new manifest putter.
+                *
+                * @param name
+                *            The name of the manifest putter
+                */
+               private ManifestPutter(String name) {
+                       this.name = name;
+               }
+
+               /**
+                * Returns the name of the manifest putter.
+                *
+                * @return The name of the manifest putter
+                */
+               public String getName() {
+                       return name;
+               }
+
+               //
+               // OBJECT METHODS
+               //
+
+               /**
+                * {@inheritDoc}
+                */
+               @Override
+               public String toString() {
+                       return name.substring(0, 1).toUpperCase() + name.substring(1);
+               }
+
+       }
+
+       /** The default file of the directory. */
+       protected String defaultName;
+
+       /** The manifest putter to use. */
+       private ManifestPutter manifestPutter;
+
+       /**
+        * 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;
+       }
+
+       /**
+        * Returns the current manifest putter.
+        *
+        * @return The current manifest putter (may be {@code null})
+        */
+       public ManifestPutter getManifestPutter() {
+               return manifestPutter;
+       }
+
+       /**
+        * Sets the manifest putter for the “ClientPutDir” command. If {@code null}
+        * is given the node will choose a manifest putter.
+        *
+        * @param manifestPutter
+        *            The manifest putter to use for the command (may be
+        *            {@code null})
+        * @return This ClientPutDir command
+        */
+       @SuppressWarnings("unchecked")
+       public C setManifestPutter(ManifestPutter manifestPutter) {
+               this.manifestPutter = manifestPutter;
+               return (C) this;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void write(Writer writer) throws IOException {
+               super.write(writer);
+               if (defaultName != null)
+                       writer.write("DefaultName=" + defaultName + LINEFEED);
+               if (manifestPutter != null) {
+                       writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED);
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Command.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Command.java
new file mode 100644 (file)
index 0000000..63aaa9c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * jSite - Command.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/Connection.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Connection.java
new file mode 100644 (file)
index 0000000..c4cb669
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * jSite - Connection.java - Copyright © 2006–2012 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.Closer;
+import de.todesbaum.util.io.LineInputStream;
+import de.todesbaum.util.io.StreamCopier;
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
+import de.todesbaum.util.io.TempFileInputStream;
+
+/**
+ * A physical connection to a Freenet node.
+ *
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id$
+ */
+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;
+
+       /** The temp directory to use. */
+       private String tempDirectory;
+
+       /**
+        * 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;
+       }
+
+       /**
+        * Sets the temp directory to use for creation of temporary files.
+        *
+        * @param tempDirectory
+        *            The temp directory to use, or {@code null} to use the default
+        *            temp directory
+        */
+       public void setTempDirectory(String tempDirectory) {
+               this.tempDirectory = tempDirectory;
+       }
+
+       /**
+        * 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 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() {
+               Closer.close(nodeWriter);
+               nodeWriter = null;
+               Closer.close(nodeOutputStream);
+               nodeOutputStream = null;
+               Closer.close(nodeInputStream);
+               nodeInputStream = null;
+               nodeInputStream = null;
+               Closer.close(nodeSocket);
+               nodeSocket = null;
+               synchronized (this) {
+                       notify();
+               }
+               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 {
+               execute(command, null);
+       }
+
+       /**
+        * Executes the specified command.
+        *
+        * @param command
+        *            The command to execute
+        * @param progressListener
+        *            A progress listener for a payload transfer
+        * @throws IllegalStateException
+        *             if the connection is not connected
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public synchronized void execute(Command command, ProgressListener progressListener) 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()) {
+                       InputStream payloadInputStream = null;
+                       try {
+                               payloadInputStream = command.getPayload();
+                               StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength(), progressListener);
+                       } finally {
+                               Closer.close(payloadInputStream);
+                       }
+                       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$
+        */
+       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.
+                */
+               @SuppressWarnings("synthetic-access")
+               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", (tempDirectory != null) ? new File(tempDirectory) : null);
+                                                       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/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java
new file mode 100644 (file)
index 0000000..8e55a2d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * jSite - ConnectionListener.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java
new file mode 100644 (file)
index 0000000..9b12e74
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * jSite - DirectFileEntry.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java
new file mode 100644 (file)
index 0000000..bf036b4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * jSite - DiskFileEntry.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java
new file mode 100644 (file)
index 0000000..7adb11f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * jSite - FileEntry.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java b/src/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java
new file mode 100644 (file)
index 0000000..84e4bf6
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * jSite - GenerateSSK.java - Copyright © 2006–2012 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$
+ */
+public class GenerateSSK extends Command {
+
+       /**
+        * Creates a new <code>GenerateSSK</code> request.
+        */
+       public GenerateSSK() {
+               super("GenerateSSK", null);
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Message.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Message.java
new file mode 100644 (file)
index 0000000..6c934ec
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * jSite - Message.java - Copyright © 2006–2012 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.Map.Entry;
+import java.util.Set;
+
+/**
+ * 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$
+ * @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
+        */
+       @Override
+       public String toString() {
+               return name + "[identifier=" + identifier + ",parameters=" + parameters.toString() + "]";
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Node.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Node.java
new file mode 100644 (file)
index 0000000..d2fa91c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * jSite - Node.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java
new file mode 100644 (file)
index 0000000..55e73f8
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * jSite - Persistence.java - Copyright © 2006–2012 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;
+
+/**
+ * The possible persistence options. This specify whether (and for how long) the
+ * node remembers to execute a request and the results. Possible values are
+ * <code>connection</code>, <code>reboot</code>, and <code>forever</code>.
+ * <code>connection</code> means that a request is aborted as soon as the
+ * connection to the node is severed. <code>reboot</code> means that a request
+ * is remembered as long as the node is running but not after restarts.
+ * <code>forever</code> finally means that a request persists until it is
+ * explicitely deleted.
+ *
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id$
+ * @see de.todesbaum.util.freenet.fcp2.ModifyPersistentRequest
+ * @see de.todesbaum.util.freenet.fcp2.RemovePersistentRequest
+ */
+public final class Persistence {
+
+       /**
+        * Denotes that a request should be terminated if the connection to the node
+        * is severed.
+        */
+       public static final Persistence CONNECTION = new Persistence("connection");
+
+       /** Denotes that a request should be remembered until the node is restarted. */
+       public static final Persistence REBOOT = new Persistence("reboot");
+
+       /**
+        * Denotes that a request should be remembered until it is explicitely
+        * deleted.
+        */
+       public static final Persistence FOREVER = new Persistence("forever");
+
+       /** The name of this persistence option. */
+       private String name;
+
+       /**
+        * Private constructor that creates a persistence option with the specified
+        * name.
+        *
+        * @param name
+        *            The name of the persistence option.
+        */
+       private Persistence(String name) {
+               this.name = name;
+       }
+
+       /**
+        * Returns the name of this persistence option.
+        *
+        * @return The name of this persistence option
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Returns a textual representation of this persistence option. The result
+        * is identical to calling {@link #getName()}.
+        *
+        * @return The name of this persistence option
+        */
+       @Override
+       public String toString() {
+               return name;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java b/src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java
new file mode 100644 (file)
index 0000000..623bc5b
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * jSite - PriorityClass.java - Copyright © 2006–2012 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;
+
+/**
+ * The possible priority classes. Possible values are, in order of descending
+ * priority: <code>maximum</code> (anything more important than fproxy),
+ * <code>interactive</code> (fproxy), <code>semi-interactive</code> (fproxy
+ * immediate mode large file downloads, not to disk), <code>updatable</code>
+ * (updatable site checks), <code>bulk</code> (large file downloads to disk),
+ * <code>prefetch</code>, <code>minimum</code>.
+ *
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id$
+ */
+public final class PriorityClass {
+
+       /** Denotes <code>maximum</code> priority class. */
+       public static final PriorityClass MAXIMUM = new PriorityClass("maximum", 0);
+
+       /** Denotes <code>interactive</code> priority class. */
+       public static final PriorityClass INTERACTIVE = new PriorityClass("interactive", 1);
+
+       /** Denotes <code>semi-interactive</code> priority class. */
+       public static final PriorityClass SEMI_INTERACTIVE = new PriorityClass("semiInteractive", 2);
+
+       /** Denotes <code>updatable</code> priority class. */
+       public static final PriorityClass UPDATABLE = new PriorityClass("updatable", 3);
+
+       /** Denotes <code>bulk</code> priority class. */
+       public static final PriorityClass BULK = new PriorityClass("bulk", 4);
+
+       /** Denotes <code>prefetch</code> priority class. */
+       public static final PriorityClass PREFETCH = new PriorityClass("prefetch", 5);
+
+       /** Denotes <code>minimum</code> priority class. */
+       public static final PriorityClass MINIMUM = new PriorityClass("minimum", 6);
+
+       /** The name of the priority class. */
+       private String name;
+
+       /** The value of the priority class. */
+       private int value;
+
+       /**
+        * Creates a new priority class with the specified name and value.
+        *
+        * @param name
+        *            The name of the priority class
+        * @param value
+        *            The value of the priority class
+        */
+       private PriorityClass(String name, int value) {
+               this.name = name;
+               this.value = value;
+       }
+
+       /**
+        * Returns the name of this priority class.
+        *
+        * @return The name of this priority class
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Returns the value of this priority class.
+        *
+        * @return The value of this priority class
+        */
+       public int getValue() {
+               return value;
+       }
+
+       //
+       // STATIC METHODS
+       //
+
+       /**
+        * Returns the priority class with the given name, matched case-insensitive.
+        *
+        * @param value
+        *            The name of the priority
+        * @return The priority with the given name, or {@code null} if no priority
+        *         matches the given name
+        */
+       public static PriorityClass valueOf(String value) {
+               for (PriorityClass priorityClass : new PriorityClass[] { MINIMUM, PREFETCH, BULK, UPDATABLE, SEMI_INTERACTIVE, INTERACTIVE, MAXIMUM }) {
+                       if (priorityClass.getName().equalsIgnoreCase(value)) {
+                               return priorityClass;
+                       }
+               }
+               return null;
+       }
+
+       //
+       // OBJECT METHODS
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String toString() {
+               return name;
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java
new file mode 100644 (file)
index 0000000..06a2171
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * jSite - RedirectFileEntry.java - Copyright © 2006–2012 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/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java
new file mode 100644 (file)
index 0000000..6f2ed7a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * jSite - ReturnType.java - Copyright © 2008–2012 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;
+
+/**
+ * Enumeration for the different return types a {@link ClientGet} request can
+ * have.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public enum ReturnType {
+
+       /** The data is returned as payload. */
+       direct,
+
+       /** The data is written to disk. */
+       disk,
+
+       /** The data is not returned at all. */
+       none
+
+}
diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java
new file mode 100644 (file)
index 0000000..e59b6a3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * jSite - Verbosity.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/image/IconLoader.java b/src/main/java/de/todesbaum/util/image/IconLoader.java
new file mode 100644 (file)
index 0000000..ff6441a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * jSite - IconLoader.java - Copyright © 2006–2012 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.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$
+ */
+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/main/java/de/todesbaum/util/io/Closer.java b/src/main/java/de/todesbaum/util/io/Closer.java
new file mode 100644 (file)
index 0000000..67f7c57
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * jSite - Closer.java - Copyright © 2006–2012 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * Helper class that can close all kinds of resources without throwing exception
+ * so that clean-up code can be written with less code. All methods check that
+ * the given resource is not <code>null</code> before invoking the close()
+ * method of the respective type.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David &lsquo;Bombe&squo;
+ *         Roden</a>
+ * @version $Id$
+ */
+public class Closer {
+
+       /**
+        * Closes the given result set.
+        *
+        * @param resultSet
+        *            The result set to close
+        * @see ResultSet#close()
+        */
+       public static void close(ResultSet resultSet) {
+               if (resultSet != null) {
+                       try {
+                               resultSet.close();
+                       } catch (SQLException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given statement.
+        *
+        * @param statement
+        *            The statement to close
+        * @see Statement#close()
+        */
+       public static void close(Statement statement) {
+               if (statement != null) {
+                       try {
+                               statement.close();
+                       } catch (SQLException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given connection.
+        *
+        * @param connection
+        *            The connection to close
+        * @see Connection#close()
+        */
+       public static void close(Connection connection) {
+               if (connection != null) {
+                       try {
+                               connection.close();
+                       } catch (SQLException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given server socket.
+        *
+        * @param serverSocket
+        *            The server socket to close
+        * @see ServerSocket#close()
+        */
+       public static void close(ServerSocket serverSocket) {
+               if (serverSocket != null) {
+                       try {
+                               serverSocket.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given socket.
+        *
+        * @param socket
+        *            The socket to close
+        * @see Socket#close()
+        */
+       public static void close(Socket socket) {
+               if (socket != null) {
+                       try {
+                               socket.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given input stream.
+        *
+        * @param inputStream
+        *            The input stream to close
+        * @see InputStream#close()
+        */
+       public static void close(InputStream inputStream) {
+               if (inputStream != null) {
+                       try {
+                               inputStream.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given output stream.
+        *
+        * @param outputStream
+        *            The output stream to close
+        * @see OutputStream#close()
+        */
+       public static void close(OutputStream outputStream) {
+               if (outputStream != null) {
+                       try {
+                               outputStream.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given reader.
+        *
+        * @param reader
+        *            The reader to close
+        * @see Reader#close()
+        */
+       public static void close(Reader reader) {
+               if (reader != null) {
+                       try {
+                               reader.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+       /**
+        * Closes the given writer.
+        *
+        * @param writer
+        *            The write to close
+        * @see Writer#close()
+        */
+       public static void close(Writer writer) {
+               if (writer != null) {
+                       try {
+                               writer.close();
+                       } catch (IOException ioe1) {
+                       }
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/io/LineInputStream.java b/src/main/java/de/todesbaum/util/io/LineInputStream.java
new file mode 100644 (file)
index 0000000..4fe0d07
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * jSite - LineInputStream.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/io/StreamCopier.java b/src/main/java/de/todesbaum/util/io/StreamCopier.java
new file mode 100644 (file)
index 0000000..c7d99de
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * jSite - StreamCopier.java - Copyright © 2006–2012 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.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.EventListener;
+
+/**
+ * Copies input from an {@link InputStream} to an {@link OutputStream}.
+ *
+ * @author <a href="mailto:droden@gmail.com">David Roden</a>
+ * @version $Id$
+ */
+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 the stream data. If the input stream is depleted before the
+        * requested number of bytes have been read an {@link EOFException} is
+        * thrown.
+        *
+        * @param progressListener
+        *            The progress listener (may be {@code null})
+        * @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(ProgressListener progressListener) throws EOFException, IOException {
+               copy(inputStream, outputStream, length, bufferSize, progressListener);
+       }
+
+       /**
+        * 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>.
+        *
+        * @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 progressListener
+        *            The progress listener (may be {@code null})
+        * @throws IOException
+        *             if an I/O exception occurs
+        */
+       public static void copy(InputStream inputStream, OutputStream outputStream, long length, ProgressListener progressListener) throws IOException {
+               copy(inputStream, outputStream, length, BUFFER_SIZE, progressListener);
+       }
+
+       /**
+        * 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 {
+               copy(inputStream, outputStream, length, bufferSize, null);
+       }
+
+       /**
+        * 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
+        * @param progressListener
+        *            The progress listener (may be {@code null})
+        * @throws IOException
+        *             if an I/O exception occurs
+        */
+       public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize, ProgressListener progressListener) 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;
+                       if (progressListener != null) {
+                               progressListener.onProgress(length - remaining, length);
+                       }
+               }
+       }
+
+       /**
+        * Interface for objects that want to be notified about the progress of a
+        * {@link StreamCopier#copy()} operation.
+        *
+        * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+        */
+       public static interface ProgressListener extends EventListener {
+
+               /**
+                * Notifiies a listener that a copy process made some progress.
+                *
+                * @param copied
+                *            The number of bytes that have already been copied
+                * @param length
+                *            The total number of bytes that will be copied
+                */
+               public void onProgress(long copied, long length);
+
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/io/TeeOutputStream.java b/src/main/java/de/todesbaum/util/io/TeeOutputStream.java
new file mode 100644 (file)
index 0000000..78860dc
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * jSite - TeeOutputStream.java - Copyright © 2010 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.todesbaum.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * {@link OutputStream} that sends all data it receives to multiple other output
+ * streams. If an error occurs during a {@link #write(int)} to one of the
+ * underlying output streams no guarantees are made about how much data is sent
+ * to each stream, i.e. there is no good way to recover from such an error.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class TeeOutputStream extends OutputStream {
+
+       /** The output streams. */
+       private final OutputStream[] outputStreams;
+
+       /**
+        * Creates a new tee output stream that sends all to all given output
+        * streams.
+        *
+        * @param outputStreams
+        *            The output streams to send all data to
+        */
+       public TeeOutputStream(OutputStream... outputStreams) {
+               this.outputStreams = outputStreams;
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * An effort is made to close all output streams. If multiple exceptions
+        * occur, only the first exception is thrown after all output streams have
+        * been tried to close.
+        */
+       @Override
+       public void close() throws IOException {
+               IOException occuredException = null;
+               for (OutputStream outputStream : outputStreams) {
+                       try {
+                               outputStream.flush();
+                       } catch (IOException ioe1) {
+                               if (occuredException == null) {
+                                       occuredException = ioe1;
+                               }
+                       }
+               }
+               if (occuredException != null) {
+                       throw occuredException;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        * <p>
+        * An effort is made to flush all output streams. If multiple exceptions
+        * occur, only the first exception is thrown after all output streams have
+        * been tried to flush.
+        */
+       @Override
+       public void flush() throws IOException {
+               IOException occuredException = null;
+               for (OutputStream outputStream : outputStreams) {
+                       try {
+                               outputStream.flush();
+                       } catch (IOException ioe1) {
+                               if (occuredException == null) {
+                                       occuredException = ioe1;
+                               }
+                       }
+               }
+               if (occuredException != null) {
+                       throw occuredException;
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void write(byte[] buffer) throws IOException {
+               for (OutputStream outputStream : outputStreams) {
+                       outputStream.write(buffer);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void write(byte[] buffer, int offset, int length) throws IOException {
+               for (OutputStream outputStream : outputStreams) {
+                       outputStream.write(buffer, offset, length);
+               }
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void write(int data) throws IOException {
+               for (OutputStream outputStream : outputStreams) {
+                       outputStream.write(data);
+               }
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/io/TempFileInputStream.java b/src/main/java/de/todesbaum/util/io/TempFileInputStream.java
new file mode 100644 (file)
index 0000000..43e2047
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * jSite - TempFileInputStream.java - Copyright © 2006–2012 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);
+               tempFile = file;
+       }
+
+       @Override
+       public void close() throws IOException {
+               super.close();
+               tempFile.delete();
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java b/src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java
new file mode 100644 (file)
index 0000000..9e380a5
--- /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/main/java/de/todesbaum/util/swing/SortedListModel.java b/src/main/java/de/todesbaum/util/swing/SortedListModel.java
new file mode 100644 (file)
index 0000000..39a5ce2
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * jSite - SortedListModel.java - Copyright © 2006–2012 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;
+
+/**
+ * @param <T>
+ *            The type of the elements
+ * @author David Roden &lt;droden@gmail.com&gt;
+ */
+public class SortedListModel<T extends Comparable<T>> extends AbstractListModel implements List<T> {
+
+       /** The elements. */
+       private List<T> elements = new ArrayList<T>();
+
+       /**
+        * {@inheritDoc}
+        */
+       public int getSize() {
+               return size();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object getElementAt(int index) {
+               return elements.get(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void add(int index, T element) {
+               elements.add(index, element);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean add(T o) {
+               boolean result = elements.add(o);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean addAll(Collection<? extends T> c) {
+               boolean result = elements.addAll(c);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public boolean addAll(int index, Collection<? extends T> 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}
+        */
+       @Override
+       public boolean equals(Object o) {
+               return elements.equals(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public T get(int index) {
+               return elements.get(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       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<T> iterator() {
+               return elements.iterator();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int lastIndexOf(Object o) {
+               return elements.lastIndexOf(o);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public ListIterator<T> listIterator() {
+               return elements.listIterator();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public ListIterator<T> listIterator(int index) {
+               return elements.listIterator(index);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public T 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 T set(int index, T element) {
+               T result = elements.set(index, element);
+               Collections.sort(elements);
+               fireContentsChanged(this, 0, size());
+               return result;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public int size() {
+               return elements.size();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public List<T> subList(int fromIndex, int toIndex) {
+               return elements.subList(fromIndex, toIndex);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public Object[] toArray() {
+               return elements.toArray();
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public <U extends Object> U[] toArray(U[] a) {
+               return elements.toArray(a);
+       }
+
+}
diff --git a/src/main/java/de/todesbaum/util/swing/TLabel.java b/src/main/java/de/todesbaum/util/swing/TLabel.java
new file mode 100644 (file)
index 0000000..0393f5c
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * jSite - TLabel.java - Copyright © 2006–2012 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$
+ */
+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/main/java/de/todesbaum/util/swing/TWizard.java b/src/main/java/de/todesbaum/util/swing/TWizard.java
new file mode 100644 (file)
index 0000000..464ac69
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * jSite - TWizard.java - Copyright © 2006–2012 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$
+ */
+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;
+
+       @Override
+       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/main/java/de/todesbaum/util/swing/TWizardPage.java b/src/main/java/de/todesbaum/util/swing/TWizardPage.java
new file mode 100644 (file)
index 0000000..a10b475
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * jSite - TWizardPage.java - Copyright © 2006–2012 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$
+ */
+public class TWizardPage extends JPanel {
+
+       protected final TWizard wizard;
+       protected String heading;
+       protected String description;
+
+       public TWizardPage(final TWizard wizard) {
+               this.wizard = wizard;
+       }
+
+       public TWizardPage(final TWizard wizard, String heading) {
+               this.wizard = wizard;
+               this.heading = heading;
+       }
+
+       public TWizardPage(final TWizard wizard, String heading, String description) {
+               this(wizard, 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/main/java/de/todesbaum/util/swing/WizardListener.java b/src/main/java/de/todesbaum/util/swing/WizardListener.java
new file mode 100644 (file)
index 0000000..987075d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * jSite - WizardListener.java - Copyright © 2006–2012 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$
+ */
+public interface WizardListener extends EventListener {
+
+       public void wizardNextPressed(TWizard wizard);
+       public void wizardPreviousPressed(TWizard wizard);
+       public void wizardQuitPressed(TWizard wizard);
+
+}
diff --git a/src/main/java/de/todesbaum/util/xml/SimpleXML.java b/src/main/java/de/todesbaum/util/xml/SimpleXML.java
new file mode 100644 (file)
index 0000000..8351581
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * jSite - SimpleXML.java - Copyright © 2006–2012 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.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;
+       }
+
+       /**
+        * Returns the value of this node. If the node does not have a value, the
+        * given default value is returned.
+        *
+        *@param defaultValue
+        *            The default value to return if the node does not have a value
+        * @return The value of this node
+        */
+       public String getValue(String defaultValue) {
+               return (value == null) ? defaultValue : 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"))) {
+                               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/main/java/de/todesbaum/util/xml/XML.java b/src/main/java/de/todesbaum/util/xml/XML.java
new file mode 100644 (file)
index 0000000..8b6b259
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * jSite - XML.java - Copyright © 2006–2012 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.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;
+       }
+
+}
diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite.properties
new file mode 100644 (file)
index 0000000..cd80c67
--- /dev/null
@@ -0,0 +1,196 @@
+#
+# jSite - jSite.properties - Copyright © 2006–2012 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>
+
+# Attention, translators! Most of the strings here are used directly.
+# However, some of them are parsed by MessageFormat
+# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
+# and thus have to adhere to some rules (check the URL above). This is the
+# case when a line contains placeholders like {0} or {0,number}! In these
+# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
+# otherwise the placeholder will not be replaced!
+
+jsite.general.ok=OK
+jsite.general.cancel=Cancel
+
+jsite.wizard.previous=Previous
+jsite.wizard.next=Next
+jsite.wizard.quit=Quit
+
+jsite.quit.question=Do you really want to quit?
+jsite.quit.question.title=Really quit?
+jsite.quit.overwrite-configuration=<html><b>Overwrite configuration?</b><br><br>A configuration file already exists:<br><code>{0}</code><br><br>Should it be overwritten?</html>
+jsite.quit.overwrite-configuration.title=Overwrite configuration?
+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.en=English
+jsite.menu.language.de=Deutsch
+jsite.menu.language.fr=Fran\u00e7ais
+jsite.menu.language.it=Italiano
+jsite.menu.language.pl=Polski
+jsite.menu.nodes=Nodes
+jsite.menu.nodes.manage-nodes=Manage nodes
+jsite.menu.options=Options
+jsite.menu.options.preferences=Preferences
+jsite.menu.help=Help
+jsite.menu.help.check-for-updates=Check for Updates
+jsite.menu.help.about=About
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 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.preferences.heading=Preferences
+jsite.preferences.description=Use this page to manage some global settings.
+jsite.preferences.temp-directory=Directory for temporary files
+jsite.preferences.temp-directory.default=Default (chosen by system)
+jsite.preferences.temp-directory.custom=Custom
+jsite.preferences.temp-directory.choose=Choose
+jsite.preferences.temp-directory.choose.approve=Choose
+jsite.preferences.config-directory=Location of configuration file
+jsite.preferences.config-directory.jar=Next to the JAR file
+jsite.preferences.config-directory.home=Home directory
+jsite.preferences.config-directory.custom=Custom directory
+jsite.preferences.insert-options=Insert options
+jsite.preferences.insert-options.use-early-encode=Generate final URI early
+jsite.preferences.insert-options.priority=Priority
+jsite.preferences.insert-options.manifest-putter=Manifest Putter
+
+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.starting=Starting\u2026
+jsite.insert.done=Done.
+jsite.insert.done.title=Insert done
+jsite.insert.insert-aborted=The insert was aborted.
+jsite.insert.insert-aborted.title=Insert Aborted
+jsite.insert.progress=Progress
+jsite.insert.k-per-s=KB/s
+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.insert-failed.title=Insert Failed
+jsite.insert.inserted=<html><b>Project inserted</b><br><br>Your project was inserted successfully.</html>
+jsite.insert.okay-copy-uri=Copy URI to Clipboard
+jsite.insert.reinserted-edition=<html><b>Edition Reinserted</b><br><br>The edition you are just inserting<br>has already been inserted before.</html>
+jsite.insert.reinserted-edition.title=Edition Reinserted
+
+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.action.copy-uri=Copy URI to Clipboard
+jsite.project.action.copy-uri.tooltip=Copies the URI of the project to the clipboard
+jsite.project.action.manage-keys=Manage Keys
+jsite.project.action.manage-keys.tooltip=Manages the keys of this project
+jsite.project.action.reset-edition=Reset Edition
+jsite.project.action.reset-edition.tooltip=Resets the edition number of the 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.path=Freesite Path
+jsite.project.project.edition=Edition
+jsite.project.project.uri=URI
+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.generate-new-key=<html><b>Generate new key?</b><br><br>If you generate a new key, your site will be published<br>under that new key. Any trust that other users put<br>in the old key of your site will be gone!<br>Also, the edition will be reset.</html>
+jsite.project.warning.reset-edition=<html><b>Reset edition?</b><br><br>Resetting the edition can lead to insert failures<br>and lots of confusion if you have not changed<br>the path or the keys of the project!</html>
+jsite.project.warning.use-clipboard-now=<html><b>URI copied</b><br><br>Please note that it is possible that quitting jSite<br>now will empty the clipboard. Please use the<br>copied URI immediately in another window!</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.ignore-hidden-files=Ignore hidden files
+jsite.project-files.ignore-hidden-files.tooltip=When selected, hidden files are not inserted
+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 you do not want to insert this file
+jsite.project-files.force-insert=Force insert
+jsite.project-files.force-insert.tooltip=Forces the insert of this file even it is not modified
+jsite.project-files.insert-redirect=Redirect
+jsite.project-files.insert-redirect.tooltip=Check if you want to insert a redirect for this file
+jsite.project-files.custom-key=Custom key
+jsite.project-files.custom-key.tooltip=The externally created key for the file
+jsite.project-files.rename=Rename
+jsite.project-files.rename.tooltip=Renames the file in the uploaded site
+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.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 not accessible.<br>Please go back and select the correct directory.</html>
+jsite.project-files.insert-now=Insert now
+jsite.project-files.invalid-default-file=Only files in the root directory may be selected as default files.
+
+jsite.update-checker.found-version.title=Found New Version
+jsite.update-checker.found-version.message=<html>A new version was found.<br><br>Version {0} (released {1,date})</html>
+jsite.update-checker.latest-version.title=Update Check
+jsite.update-checker.latest-version.newer.message=<html>You are running version {0} but a newer<br>version ({1}) has been found!</html>
+jsite.update-checker.latest-version.older.message=<html>You are running version {0} but the<br>latest version seems to be {1}.</html>
+jsite.update-checker.latest-version.okay.message=<html>You are currently running version {0}<br>which is the latest version.</html>
+
+jsite.key-dialog.title=Manage Project Keys
+jsite.key-dialog.button.ok.tooltip=Accepts the changes
+jsite.key-dialog.button.cancel.tooltip=Discards the changes
+jsite.key-dialog.button.generate=Regenerate Keys
+jsite.key-dialog.button.generate.tooltip=Create a new key pair
+jsite.key-dialog.label.keys=<html><b>Keys</b></html>
+jsite.key-dialog.label.private-key=Private Key
+jsite.key-dialog.label.public-key=Public Key
+jsite.key-dialog.label.actions=<html><b>Actions</b></html>
+
+jsite.warning.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.warning.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.warning.site-larger-than-2-mib=<html><b>Site is larger than 2 MiB!</b><br><br>Your site contains more than 2 megabytes of data.<br>Due to bugs in Freenet it will probably not load correctly.<br>Try to reduce the size of your site, or continue at your own peril.</html>
+
+jsite.error.no-node-selected=<html><b>No node selected</b><br><br>Please select a node from the menu!</html>
+jsite.error.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.error.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.error.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.error.index-missing=<html>Your default file is missing</b><br><br>A default file was previously specified but it<br>does not exist anymore! Please select<br>a new default file in the list of files.</html>
+jsite.error.index-not-inserted=<html><b>Default file not inserted</b><br><br>You have chosen not to insert the default file!<br>You need to either choose to insert it or select<br>a different default file!</html>
+jsite.error.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.error.no-files-to-insert=<html><b>No files to insert</b><br><br>You do not have any files selected for insertion!<br>Please select at least one file to insert.</html>
+jsite.error.duplicate-file=<html><b>Duplicate file</b><br><br>The file <code>{0}</code> is inserted twice!<br>Please check your filenames and redirects.</html>
diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties
new file mode 100644 (file)
index 0000000..8641d14
--- /dev/null
@@ -0,0 +1,196 @@
+#
+# jSite - jSite_de.properties - Copyright © 2006–2012 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>
+
+# Attention, translators! Most of the strings here are used directly.
+# However, some of them are parsed by MessageFormat
+# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
+# and thus have to adhere to some rules (check the URL above). This is the
+# case when a line contains placeholders like {0} or {0,number}! In these
+# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
+# otherwise the placeholder will not be replaced!
+
+jsite.general.ok=OK
+jsite.general.cancel=Abbrechen
+
+jsite.wizard.previous=Zur\u00fcck
+jsite.wizard.next=Vorw\u00e4rts
+jsite.wizard.quit=Beenden
+
+jsite.quit.question=M\u00f6chten Sie jSite wirklich beenden?
+jsite.quit.question.title=Wirklich beenden?
+jsite.quit.overwrite-configuration=<html><b>Konfiguration \u00fcberschreiben?</b><br><br>Es existiert bereits eine Konfigurationsdatei unter:<br><code>{0}</code><br><br>Soll sie \u00fcberschrieben werden?</html>
+jsite.quit.overwrite-configuration.title=Konfiguration \u00fcberschreiben?
+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.en=English
+jsite.menu.language.de=Deutsch
+jsite.menu.language.fr=Fran\u00e7ais
+jsite.menu.language.it=Italiano
+jsite.menu.language.pl=Polski
+jsite.menu.nodes=Nodes
+jsite.menu.nodes.manage-nodes=Nodes verwalten
+jsite.menu.options=Optionen
+jsite.menu.options.preferences=Einstellungen
+jsite.menu.help=Hilfe
+jsite.menu.help.check-for-updates=Auf Updates pr\u00fcfen
+jsite.menu.help.about=\u00dcber
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 David Roden<br>Ver\u00f6ffentlicht 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\u00fcgen
+jsite.node-manager.new-node=Neuer Node
+jsite.node-manager.delete-node=Node l\u00f6schen
+jsite.node-manager.delete-node.warning=<html><b>Nodel\u00f6schung best\u00e4tigen</b><br><br>Wollen Sie diesen Node wirklich l\u00f6schen?</html>
+jsite.node-manager.name=Name
+jsite.node-manager.hostname=Hostname
+jsite.node-manager.port=Port
+
+jsite.preferences.heading=Einstellungen
+jsite.preferences.description=Auf dieser Seite k\u00f6nnen einige globale Einstellungen vorgenommen werden.
+jsite.preferences.temp-directory=Verzeichnis f\u00fcr tempor\u00e4re Dateien
+jsite.preferences.temp-directory.default=Standard (vom System bestimmt)
+jsite.preferences.temp-directory.custom=Eigenes
+jsite.preferences.temp-directory.choose=Ausw\u00e4hlen
+jsite.preferences.temp-directory.choose.approve=Ausw\u00e4hlen
+jsite.preferences.config-directory=Lage der Konfigurationsdatei
+jsite.preferences.config-directory.jar=Neben der JAR-Datei
+jsite.preferences.config-directory.home=Benutzerverzeichnis
+jsite.preferences.config-directory.custom=Angegebenes Verzeichnis
+jsite.preferences.insert-options=Einf\u00fcgeoptionen
+jsite.preferences.insert-options.use-early-encode=Endg\u00fcltige URI fr\u00fcher berechnen
+jsite.preferences.insert-options.priority=Priorit\u00e4t
+jsite.preferences.insert-options.manifest-putter=Manifesterstellung
+
+jsite.insert.heading=Projekt einf\u00fcgen
+jsite.insert.description=Bitte warten Sie, w\u00e4hrend das Projekt eingef\u00fcgt wird.
+jsite.insert.project-information=Projektinformationen
+jsite.insert.request-uri=Freesite
+jsite.insert.start-time=Beginn
+jsite.insert.starting=Beginne\u2026
+jsite.insert.done=Fertig.
+jsite.insert.done.title=Einf\u00fcgen abgeschlossen
+jsite.insert.insert-aborted=Das Einf\u00fcgen wurde abgebrochen.
+jsite.insert.insert-aborted.title=Einf\u00fcgen abgebrochen
+jsite.insert.progress=Fortschritt
+jsite.insert.k-per-s=KB/s
+jsite.insert.insert-failed=<html><b>Einf\u00fcgen fehlgeschlagen</b><br><br>Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingef\u00fcgt werden konnten.</html>
+jsite.insert.insert-failed-with-cause=<html><b>Einf\u00fcgen fehlgeschlagen</b><br><br>Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da<br>einige Dateien nicht eingef\u00fcgt werden konnten.<br>Folgender Fehler trat auf:<br><br><code>{0}</code></html>
+jsite.insert.insert-failed.title=Einf\u00fcgen fehlgeschlagen
+jsite.insert.inserted=<html><b>Projekt eingef\u00fcgt</b><br><br>Ihr Projekt wurde erfolgreich eingef\u00fcgt.</html>
+jsite.insert.okay-copy-uri=URI kopieren
+jsite.insert.reinserted-edition=<html><b>Edition bereits eingef\u00fcgt</b><br><br>Die Edition, die gerade eingef\u00fcgt wird,<br>ist schon einmal eingef\u00fcgt worden.</html>
+jsite.insert.reinserted-edition.title=Edition bereits eingef\u00fcgt
+
+jsite.file-scanner.can-not-read-directory=Kann Verzeichnis nicht lesen
+
+jsite.project.heading=Projekt ausw\u00e4hlen
+jsite.project.description=W\u00e4hlen Sie das Projekt aus, welches sie einf\u00fcgen m\u00f6chten, oder erstellen Sie ein neues Projekt.
+jsite.project.action.browse=Durchsuchen
+jsite.project.action.browse.choose=Ausw\u00e4hlen
+jsite.project.action.browse.tooltip=Lokalen Pfad f\u00fcr Projekt ausw\u00e4hlen
+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\u00f6schen
+jsite.project.action.delete-project.tooltip=Ein Projekt l\u00f6schen
+jsite.project.action.delete-project.confirm=<html><b>L\u00f6schung best\u00e4tigen</b><br><br>Das Projekt \u201e{0}\u201c wird gel\u00f6scht!<br>M\u00f6chten 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\u00e4hlte Projekt duplizieren
+jsite.project.action.copy-uri=URI kopieren
+jsite.project.action.copy-uri.tooltip=Kopiert die URI des ausgew\u00e4hlten Projektes in die Zwischenablage
+jsite.project.action.manage-keys=Schl\u00fcsselverwaltung
+jsite.project.action.manage-keys.tooltip=Verwaltet die Schl\u00fcssel des Projekts
+jsite.project.action.reset-edition=Edition zur\u00fccksetzen
+jsite.project.action.reset-edition.tooltip=Setzt die Editionsnummer des Projekts zur\u00fcck
+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.path=Seitenpfad
+jsite.project.project.edition=Edition
+jsite.project.project.uri=Anfrage-URI
+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\u00e4uft und dass Sie<br> den korrekten Hostnamen und die korrekte Portnummer auf der<br>\u201eNode Einstellungen\u201c Seite eingegeben haben.</html>
+jsite.project.warning.generate-new-key=<html><b>Neues Schl\u00fcsselpaar generieren?</b><br><br>Wenn Sie das Schl\u00fcsselpaar f\u00fcr das Projekt \u00e4ndern,<br>wird sich die URI f\u00fcr Ihr Projekt ebenfalls<br>\u00e4ndern, und jegliches Vertrauen, dass andere<br>Benutzer in das alte Schl\u00fcsselpaar hatten, wird<br>verloren gehen! Au\u00dferdem wird die Edition zur\u00fcckgesetzt.</html>
+jsite.project.warning.reset-edition=<html><b>Edition zur\u00fccksetzen?</b><br><br>Das Zur\u00fccksetzen der Editionsnummer kann zum<br>Fehlschlagen des Einf\u00fcgens f\u00fchren, wenn sich nicht<br>auch die URI oder der Pfad des Projekts ge\u00e4ndert haben!</html>
+jsite.project.warning.use-clipboard-now=<html><b>Anfrage-URI kopiert</b><br><br>Bitte beachten Sie, dass die Zwischenablage nach dem<br>Beenden von jSite eventuell nicht mehr die kopierte<br>URI enth\u00e4lt. Bitte f\u00fcgen Sie sie daher schleunigst in<br>ein anderes Programm ein!</html>
+
+jsite.project-files.heading=Projektdateien
+jsite.project-files.description=<html>Auf dieser Seite k\u00f6nnen Parameter f\u00fcr die einzelnen Dateien dieses Projekts angegeben werden, z.B.<br>extern erstellte Schl\u00fcssel 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.ignore-hidden-files=Versteckte Dateien ignorieren
+jsite.project-files.ignore-hidden-files.tooltip=Verhindert, dass versteckte Dateien hochgeladen werden
+jsite.project-files.file-options=Dateioptionen
+jsite.project-files.default=Index-Datei
+jsite.project-files.default.tooltip=Lege Index-Datei f\u00fcr Projekt fest
+jsite.project-files.insert=Einf\u00fcgen
+jsite.project-files.insert.tooltip=jSite f\u00fcgt diese Datei ein
+jsite.project-files.force-insert=Einf\u00fcgen erzwingen
+jsite.project-files.force-insert.tooltip=F\u00fcgt diese Datei ein, auch wenn sie nicht modifiziert wurde
+jsite.project-files.insert-redirect=Umleitung
+jsite.project-files.insert-redirect.tooltip=F\u00fcgt eine Umleitung ein
+jsite.project-files.custom-key=Extern erstellter Schl\u00fcssel
+jsite.project-files.custom-key.tooltip=Der extern erstellte Schl\u00fcssel f\u00fcr diese Datei
+jsite.project-files.rename=Umbenennen
+jsite.project-files.rename.tooltip=Benennt die Datei in der eingef\u00fcgten Seite um
+jsite.project-files.mime-type=MIME-Typ
+jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier ausw\u00e4hlen, wenn die automatische Erkennenung falsch ist
+jsite.project-files.container=Container
+jsite.project-files.container.tooltip=W\u00e4hlt einen Container f\u00fcr diese Datei aus
+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\u00fcck und beheben Sie den Fehler!</html>
+jsite.project-files.insert-now=Jetzt einf\u00fcgen
+jsite.project-files.invalid-default-file=Nur Dateien im obersten Verzeichnis d\u00fcrfen als Index-Dateien ausgew\u00e4hlt werden.
+
+jsite.update-checker.found-version.title=Neue Version gefunden
+jsite.update-checker.found-version.message=<html>Eine neue Version wurde gefunden.<br><br>Version {0} (ver\u00f6ffentlicht {1,date})</html>
+jsite.update-checker.latest-version.title=Update\u00fcberpr\u00fcfung
+jsite.update-checker.latest-version.newer.message=<html>Es l\u00e4uft momentan Version {0}, aber eine<br>neue Version ({1}) wurde bereits gefunden!</html>
+jsite.update-checker.latest-version.older.message=<html>Es l\u00e4uft momentan Version {0}, aber die<br>aktuelle Version ist erst {1}.</html>
+jsite.update-checker.latest-version.okay.message=<html>Es l\u00e4uft momentan Version {0},<br>und diese Version ist aktuell.</html>
+
+jsite.key-dialog.title=Projektschl\u00fcsselverwaltung
+jsite.key-dialog.button.ok.tooltip=\u00c4nderungen akzeptieren
+jsite.key-dialog.button.cancel.tooltip=\u00c4nderungen verwerfen
+jsite.key-dialog.button.generate=Schl\u00fcssel neu generieren
+jsite.key-dialog.button.generate.tooltip=Generiert ein neues Schl\u00fcsselpaar
+jsite.key-dialog.label.keys=<html><b>Schl\u00fcssel</b></html>
+jsite.key-dialog.label.private-key=Privater Schl\u00fcssel
+jsite.key-dialog.label.public-key=\u00d6ffentlicher Schl\u00fcssel
+jsite.key-dialog.label.actions=<html><b>Aktionen</b></html>
+
+jsite.warning.empty-index=<html><b>Keine Index-Datei gew\u00e4hlt</b><br><br>Sie haben keine Index-Datei f\u00fcr das Projekt angegeben.<br>Obwohl es m\u00f6glich ist, das zu machen, sollten Sie doch<br>eine Index-Datei angeben, um das Browsen zu erleichtern.</html>
+jsite.warning.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\u00fchren.</html>
+jsite.warning.site-larger-than-2-mib=<html><b>Ihr Projekt ist gr\u00f6\u00dfer als 2 Megabyte!</b><br><br>Ihr Projekt enth\u00e4lt mehr als 2 Megabyte an Daten. Aufgrund<br>eines Fehlers in Freenet wird die Seite wahrscheinlich nicht<br>korrekt angezeigt werden. Bitte reduzieren Sie die Gr\u00f6\u00dfe<br>Ihres Projektes, oder fahren Sie auf eigene Gefahr fort.</html>
+
+jsite.error.no-node-selected=<html><b>Kein Node ausgew\u00e4hlt</b><br><br>Bitte w\u00e4hlen Sie einen Node aus dem Men\u00fc!</html>
+jsite.error.no-node-running=<html><b>Der Node l\u00e4uft nicht</b><br><br>Sie k\u00f6nnen das Projekt nicht einf\u00fcgen, wenn<br>Ihr Node nicht l\u00e4uft. Bitte starten Sie Ihren Node<br>und probieren Sie es erneut.</html>
+jsite.error.no-local-path=<html><b>Kein lokaler Pfad</b><br><br>Sie haben keinen lokalen Pfad f\u00fcr die einzuf\u00fcgenden Dateien angegeben.<br>Es ist nicht m\u00f6glich, ohne lokalen Pfad weiter zu machen.</html>
+jsite.error.no-path=<html><b>Kein Freesite-Pfad</b><br><br>Sie haben keinen Pfad f\u00fcr die Freesite angegeben.<br>Es ist nicht m\u00f6glich, ohne einen Freesite-Pfad weiter zu machen.</html>
+jsite.error.index-missing=<html><b>Index-Datei fehlt!</b><br><br>Sie haben eine Index-Datei f\u00fcr das Project gew\u00e4hlt,<br>aber diese Index-Datei existiert nicht mehr!<br>Bitte w\u00e4hlen Sie eine neue Index-Datei.</html>
+jsite.error.index-not-inserted=<html><b>Index-Datei nicht eingef\u00fcgt</b><br><br>Die index-Datei ist nicht zum Einf\u00fcgen ausgew\u00e4hlt!<br>Sie m\u00fcssen entweder w\u00e4hlen, die Index-Datei einzuf\u00fcgen,<br>oder Sie m\u00fcssen eine andere Index-Datei ausw\u00e4hlen!</html>
+jsite.error.no-custom-key=<html><b>Kein externer Schl\u00fcssel</b><br><br>Sie haben angegeben, dass die Datei <code>{0}</code><br>nicht eingef\u00fcgt werden soll. Allerdings haben Sie<br>keinen extern erstellten Schl\u00fcssel angegeben.</html>
+jsite.error.no-files-to-insert=<html><b>Keine Dateien einzuf\u00fcgen</b><br><br>Es sind keine Dateien zum Einf\u00fcgen ausgew\u00e4hlt! Bitte<br>w\u00e4hlen Sie mindestens eine Datei zum Einf\u00fcgen aus!</html>
+jsite.error.duplicate-file=<html><b>Doppelte Datei</b><br><br>Die Datei <code>{0}</code> wird zweimal eingef\u00fcgt!<br>Bitte \u00fcberpr\u00fcfen Sie Ihre Umleitungen und Dateinamen!</html>
diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties
new file mode 100644 (file)
index 0000000..6f1e8c6
--- /dev/null
@@ -0,0 +1,195 @@
+#
+# jSite - jSite_fr.properties - Copyright © 2006–2012 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.
+#
+
+# French language file by Florent Daigni\u00e8re <nextgens@freenetproject.org>, Julien Cornuwel <batosai@batosai.net>, and Clement Vollet <cvollet@gmail.com>.
+
+# Attention, translators! Most of the strings here are used directly.
+# However, some of them are parsed by MessageFormat
+# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html)
+# and thus have to adhere to some rules (check the URL above). This is the
+# case when a line contains placeholders like {0} or {0,number}! In these
+# lines single quotes (ASCII 39) needs to be escaped by entering them twice,
+# otherwise the placeholder will not be replaced!
+
+jsite.general.ok=OK
+jsite.general.cancel=Annuler
+
+jsite.wizard.previous=Pr\u00e9c\u00e9dent
+jsite.wizard.next=Suivant
+jsite.wizard.quit=Quitter
+
+jsite.quit.question=Voulez-vous r\u00e9ellement quitter?
+jsite.quit.question.title=Souhaitez vous quitter?
+jsite.quit.overwrite-configuration=<html><b>Ecraser la configuration?</b><br><br>Un fichier de configuration \u00e9xiste d\u00e9j\u00e0:<br><code>{0}</code><br><br>Doit-il \u00eatre \u00e9cras\u00e9 ?</html>
+jsite.quit.overwrite-configuration.title=Ecraser la configuration?
+jsite.quit.config-not-saved=<html><b>Configuration non sauvegard\u00e9e</b><br><br>La configuration n'a pas pu \u00eatre sauv\u00e9e.<br>Voulez vous quitter tout de m\u00eame?</html>
+
+jsite.menu.languages=Langue
+jsite.menu.language.en=English
+jsite.menu.language.de=Deutsch
+jsite.menu.language.fr=Fran\u00e7ais
+jsite.menu.language.it=Italiano
+jsite.menu.language.pl=Polski
+jsite.menu.nodes=Noeud
+jsite.menu.nodes.manage-nodes=G\u00e9rer les noeuds
+jsite.menu.options=Options
+jsite.menu.options.preferences=Pr\u00e9f\u00e9rences
+jsite.menu.help=Aide
+jsite.menu.help.check-for-updates=Mises \u00e0 jour
+jsite.menu.help.about=A propos de jSite
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 David Roden<br>Publi\u00e9 sous GNU General Public License</html>
+
+jsite.node-manager.heading=Gestionnaire de noeud
+jsite.node-manager.description=G\u00e9rez vos noeuds.
+jsite.node-manager.node-information=Information sur le noeud
+jsite.node-manager.add-node=Ajouter un noeud
+jsite.node-manager.new-node=Nouveau noeud
+jsite.node-manager.delete-node=Supprimer un noeud
+jsite.node-manager.delete-node.warning=<html><b>Confirmer la suppression</b><br><br>Supprimer r\u00e9ellement ce noeud?</html>
+jsite.node-manager.name=Nom
+jsite.node-manager.hostname=Nom de machine
+jsite.node-manager.port=Port
+
+jsite.preferences.heading=Pr\u00e9f\u00e9rences
+jsite.preferences.description=Utilisez cette page pour g\u00e9rer quelques param\u00e8tres globaux.
+jsite.preferences.temp-directory=R\u00e9pertoire des fichiers temporaires
+jsite.preferences.temp-directory.default=D\u00e9faut (choix syst\u00e8mes)
+jsite.preferences.temp-directory.custom=Personnalis\u00e9
+jsite.preferences.temp-directory.choose=Choisir
+jsite.preferences.temp-directory.choose.approve=Choisir
+jsite.preferences.config-directory=Chemin du fichier de configuration
+jsite.preferences.config-directory.jar=Suivant, le fichier JAR
+jsite.preferences.config-directory.home=Acceuil
+jsite.preferences.config-directory.custom=R\u00e9pertoire personnel
+jsite.preferences.insert-options=Options d'insertion
+jsite.preferences.insert-options.use-early-encode=G\u00e9n\u00e9rer d'abord l'URI
+jsite.preferences.insert-options.priority=Priorit\u00e9
+jsite.preferences.insert-options.manifest-putter=Ajout de Manifest
+
+jsite.insert.heading=Projet d'insertion
+jsite.insert.description=Veuillez attendre durant l'insertion du projet.
+jsite.insert.project-information=Information \u00e0 propos du projet.
+jsite.insert.request-uri=Freesite
+jsite.insert.start-time=Commenc\u00e9 \u00e0
+jsite.insert.starting=D\u00e9marrage\u2026
+jsite.insert.done=Termin\u00e9.
+jsite.insert.done.title=Insertion effectu\u00e9e
+jsite.insert.insert-aborted=L'insertion a \u00e9t\u00e9 annul\u00e9e.
+jsite.insert.insert-aborted.title=Insertion Annul\u00e9e
+jsite.insert.progress=Avancement
+jsite.insert.k-per-s=Ko/s
+jsite.insert.insert-failed=<html><b>Insertion \u00e9chou\u00e9e</b><br><br>L'insertion du projet \u00e0 \u00e9chou\u00e9e.<br>Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s.</html>
+jsite.insert.insert-failed-with-cause=<html><b>Insertion \u00e9chou\u00e9e</b><br><br>L'insertion du projet \u00e0 \u00e9chou\u00e9e.<br>Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s.<br>L'erreur suivante s'est produite:<br><br><code>{0}</code></html>
+jsite.insert.insert-failed.title=Insertion Echou\u00e9e
+jsite.insert.inserted=<html><b>Projet ins\u00e9r\u00e9!</b><br><br>Votre projet \u00e0 \u00e9t\u00e9 correctement ins\u00e9r\u00e9.</html>
+jsite.insert.okay-copy-uri=Copier l'URI vers le presse-papiers
+jsite.insert.reinserted-edition=<html><b>Edition r\u00e9ins\u00e9r\u00e9e</b><br><br>L'\u00e9dition que vous \u00eates en train d'ins\u00e9rer<br>a d\u00e9j\u00e0 \u00e9t\u00e9 ins\u00e9r\u00e9e avant.</html>
+jsite.insert.reinserted-edition.title=Edition r\u00e9ins\u00e9r\u00e9e
+
+jsite.file-scanner.can-not-read-directory=Impossible de lire le r\u00e9pertoire
+
+jsite.project.heading=S\u00e9lectionnez un projet
+jsite.project.description=S\u00e9lectionnez un projet de la liste ou cr\u00e9ez en un nouveau.
+jsite.project.action.browse=Naviguer
+jsite.project.action.browse.choose=Choisir
+jsite.project.action.browse.tooltip=Choisir un r\u00e9pertoire
+jsite.project.action.add-project=Ajouter un projet
+jsite.project.action.add-project.tooltip=Ajouter un projet
+jsite.project.new-project.name=Nouveau projet
+jsite.project.action.delete-project=D\u00e9truire le projet
+jsite.project.action.delete-project.tooltip=D\u00e9truire le projet
+jsite.project.action.delete-project.confirm=<html><b>Confirmez la suppression</b><br><br>Le projet \u201c{0}\u201d va \u00eatre d\u00e9truit!<br>Voulez vous poursuivre?</html>
+jsite.project.action.clone-project=Cloner le projet
+jsite.project.action.clone-project.copy=Copie de {0}
+jsite.project.action.clone-project.tooltip=Cloner le projet s\u00e9lectionn\u00e9
+jsite.project.action.copy-uri=Copier l'URI dans le presse-papier
+jsite.project.action.copy-uri.tooltip=Copie l'URI du projet dans le presse-papier
+jsite.project.action.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9
+jsite.project.action.generate-new-key.tooltip=Cr\u00e9e une nouvelle cl\u00e9 pour ce projet
+jsite.project.action.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition
+jsite.project.action.reset-edition.tooltip=Remettre \u00e0 z\u00e9ro l'\u00e9dition du projet
+jsite.project.project.information=Informations concernant le projet
+jsite.project.project.name=Nom
+jsite.project.project.description=Description
+jsite.project.project.local-path=Chemin local
+jsite.project.project.address=Adresse
+jsite.project.project.path=Chemin du freesite
+jsite.project.project.edition=Edition
+jsite.project.project.uri=URI
+jsite.project.keygen.io-error=<html><b>Erreur de communication avec le noeud</b><br><br>La communication avec le noeud \u00e0 \u00e9chou\u00e9e<br>Erreur:<br><br><code>{0}</code><br><br>Assurez vous que les informations saisies dans la page de configuration sont correctes.</html>
+jsite.project.warning.generate-new-key=<html><b>G\u00e9n\u00e9rer une nouvelle cl\u00e9 ?</b><br><br>Si vous g\u00e9n\u00e9rez une nouvelle cl\u00e9, votre site sera publi\u00e9<br>avec cette nouvelle cl\u00e9. La confiance que les autres<br>utilisateurs pla\u00e7aient dans l'ancienne cl\u00e9 sera perdue !</html>
+jsite.project.warning.reset-edition=<html><b>Remettre \u00e0 z\u00e9ro l'\u00e9dition ?</b><br><br>Remettre \u00e0 z\u00e9ro l'\u00e9dition peut faire \u00e9chouer l'insertion<br> ou poser des probl\u00e8mes si vous n'avez pas chang\u00e9<br>le chemin ou les cl\u00e9s du projet !</html>
+jsite.project.warning.use-clipboard-now=<html><b>URI copi\u00e9e</b><br><br>Veuillez noter qu'il est possible qu'en quittant jSite<br>maintenant le presse-papiers soit vid\u00e9. Merci d'utiliser<br>l'URI copi\u00e9e imm\u00e9diatement dans une autre fen\u00eatre !</html>
+
+jsite.project-files.heading=Fichiers du projet
+jsite.project-files.description=<html>Dans cette page vous pouvez sp\u00e9cifier les informations concernant la configuration des noeuds telles que:<br>Le type de contenu mime si l'auto d\u00e9tection \u00e0 \u00e9chou\u00e9e.</html>
+jsite.project-files.action.rescan=Re-scan
+jsite.project-files.action.rescan.tooltip=V\u00e9rifier la pr\u00e9sence de nouveau fichiers
+jsite.project-files.ignore-hidden-files=Ignorer les fichiers cach\u00e9s
+jsite.project-files.ignore-hidden-files.tooltip=Si s\u00e9lectionn\u00e9, les fichiers cach\u00e9s ne sont pas ins\u00e9r\u00e9s
+jsite.project-files.file-options=Option des fichiers
+jsite.project-files.default=Fichier par d\u00e9faut
+jsite.project-files.default.tooltip=Est-ce l'index?
+jsite.project-files.insert=Ins\u00e9rer
+jsite.project-files.insert.tooltip=D\u00e9cochez si vous ne voulez pas ins\u00e9rer ce fichier
+jsite.project-files.force-insert=Forcer l'insertion
+jsite.project-files.force-insert.tooltip=Forcer l'insertion de ce fichier tant qu'il n'est pas modifi\u00e9
+jsite.project-files.insert-redirect=Redirection
+jsite.project-files.insert-redirect.tooltip=Cochez si vous voulez ins\u00e9rer une redirection pour ce fichier
+jsite.project-files.custom-key=Clef existante
+jsite.project-files.custom-key.tooltip=Utiliser une clef existante pour ce fichier
+jsite.project-files.rename=Renommer
+jsite.project-files.rename.tooltip=Renomme le fichier dans le site ins\u00e9r\u00e9
+jsite.project-files.mime-type=MIME type
+jsite.project-files.mime-type.tooltip=S\u00e9lectionez le type MIME du fichier si la d\u00e9tection \u00e0 \u00e9chou\u00e9e
+jsite.project-files.container=Container
+jsite.project-files.container.tooltip=S\u00e9lectionnez un container pour le fichier
+jsite.project-files.scan-error=<html><b>Erreur lors du parcours des fichiers</b><br><br>Soit le r\u00e9pertoire du projet n'existe pas,<br>ou des fichiers/r\u00e9pertoires sont inaccessibles.<br>Veuillez revenir en arri\u00e8re et s\u00e9lectionner un autre r\u00e9pertoire.</html>
+jsite.project-files.insert-now=Ins\u00e9rer
+jsite.project-files.invalid-default-file=Seulement les fichiers de la racine peuvent \u00eatre selectionn\u00e9s comme fichiers par defaut
+
+jsite.update-checker.found-version.title=Nouvelle version disponible
+jsite.update-checker.found-version.message=<html>Une nouvelle version est disponible.<br><br>Version {0} (publi\u00e9e le {1,date})</html>
+jsite.update-checker.latest-version.title=Recherche de mises \u00e0 jour
+jsite.update-checker.latest-version.newer.message=<html>Vous utilisez la version {0} mais une version<br>plus r\u00e9cente ({1}) est disponible!</html>
+jsite.update-checker.latest-version.older.message=<html>Vous utilisez la version {0} mais la<br>derni\u00e8re version semble \u00eatre la {1}.</html>
+jsite.update-checker.latest-version.okay.message=<html>Vous utilisez la version {0}<br>qui semble \u00eatre la derni\u00e8re.</html>
+
+jsite.key-dialog.title=G\u00e9rer les cl\u00e9s des projets
+jsite.key-dialog.button.ok.tooltip=Accepter les changements
+jsite.key-dialog.button.cancel.tooltip=Annuler les changements
+jsite.key-dialog.button.generate=Reg\u00e9n\u00e9rer les cl\u00e9s
+jsite.key-dialog.button.generate.tooltip=Cr\u00e9er une nouvelle paire de cl\u00e9s
+jsite.key-dialog.label.keys=<html><b>Cl\u00e9s</b></html>
+jsite.key-dialog.label.private-key=Cl\u00e9 priv\u00e9e
+jsite.key-dialog.label.public-key=Cl\u00e9 publique
+jsite.key-dialog.label.actions=<html><b>Actions</b></html>
+
+jsite.warning.empty-index=<html><b>Pas de fichier par d\u00e9faut</b><br><br>Avez vous sp\u00e9cifi\u00e9 un fichier par d\u00e9faut pour le projet?<br>M\u00eame s'il est possible de ne pas en sp\u00e9cifier, c'est g\u00e9n\u00e9ralement une mauvaise id\u00e9e.</html>
+jsite.warning.index-not-html=<html><b>Le fichier principal n'est pas un fichier HTML!</b><br><br>Votre fichier par d\u00e9faut n'est pas du type MIME "text/html"!<br>Chargez ce type de fichiers dans un navigateur peut \u00eatre dangereux.</html>
+
+jsite.error.no-node-selected=<html><b>Pas de noeud s\u00e9lectionn\u00e9</b><br><br>S\u00e9lectionnez un noeud dans le menu!</html>
+jsite.error.no-node-running=<html><b>Ce noeud n'est pas actif!</b><br><br>Vous ne pouvez pas utiliser jSite sans noeud actif.<br>Veuillez d\u00e9marrer votre noeud et r\u00e9essayer.</html>
+jsite.error.no-local-path=<html><b>Pas de chemin local sp\u00e9cifi\u00e9</b><br><br>Vous avez omis de sp\u00e9cifier le chemin local \u00e0 ins\u00e9rer.</html>
+jsite.error.no-path=<html><b>Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite</b><br><br>Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite.<br>Ce champ est n\u00e9cessaire.</html>
+jsite.error.index-missing=<html>Votre fichier par d\u00e9faut est manquant</b><br><br>Un fichier par d\u00e9faut est sp\u00e9cifi\u00e9 mais il<br>n'existe plus ! S\u00e9lectionnez un nouveau<br>fichier par d\u00e9faut dans la liste.</html>
+jsite.error.index-not-inserted=<html><b>Fichier par d\u00e9faut non ins\u00e9r\u00e9</b><br><br>Vous avez choisi de ne pas ins\u00e9rer le fichier par d\u00e9faut !<br>Vous devez soit l'ins\u00e9rer soit choisir<br>un fichier par d\u00e9faut diff\u00e9rent !</html>
+jsite.error.no-custom-key=<html><b>Pas de clef existante sp\u00e9cifi\u00e9e pour ce fichier</b><br><br>Vous avez sp\u00e9cifier de ne pas ins\u00e9rer <code>{0}</code><br> mais n'avez pas sp\u00e9cifier de clef ou rediriger!</html>
+jsite.error.no-files-to-insert=<html><b>Aucun fichier \u00e0 ins\u00e9rer</b><br><br>Vous n'avez s\u00e9lectionn\u00e9 aucun fichier pour l'insertion !<br>Veuillez s\u00e9lectionner au moins un fichier \u00e0 ins\u00e9rer.</html>
+jsite.error.duplicate-file=<html><b>Fichier dupliqu\u00e9</b><br><br>Le fichier <code>{0}</code> est ins\u00e9r\u00e9 deux fois !<br>Veuillez v\u00e9rifier les noms de fichier et les redirections.</html>
diff --git a/src/main/resources/flag-de.png b/src/main/resources/flag-de.png
new file mode 100644 (file)
index 0000000..d1b572b
Binary files /dev/null and b/src/main/resources/flag-de.png differ
diff --git a/src/main/resources/flag-en.png b/src/main/resources/flag-en.png
new file mode 100644 (file)
index 0000000..33472a7
Binary files /dev/null and b/src/main/resources/flag-en.png differ
diff --git a/src/main/resources/flag-fr.png b/src/main/resources/flag-fr.png
new file mode 100644 (file)
index 0000000..5793e70
Binary files /dev/null and b/src/main/resources/flag-fr.png differ
diff --git a/src/main/resources/jsite-icon.png b/src/main/resources/jsite-icon.png
new file mode 100644 (file)
index 0000000..fcddf6c
Binary files /dev/null and b/src/main/resources/jsite-icon.png differ