Merge branch 'release-0.13' 0.13
authorDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 31 May 2016 18:26:49 +0000 (20:26 +0200)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 31 May 2016 18:26:49 +0000 (20:26 +0200)
23 files changed:
pom.xml
src/main/java/de/todesbaum/jsite/application/FileOption.java
src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java
src/main/java/de/todesbaum/jsite/application/Project.java
src/main/java/de/todesbaum/jsite/application/ProjectInserter.java
src/main/java/de/todesbaum/jsite/application/UpdateChecker.java
src/main/java/de/todesbaum/jsite/gui/FileScanner.java
src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java
src/main/java/de/todesbaum/jsite/gui/KeyDialog.java
src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java
src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java
src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java
src/main/java/de/todesbaum/jsite/gui/ScannedFile.java [new file with mode: 0644]
src/main/java/de/todesbaum/jsite/main/CLI.java
src/main/java/de/todesbaum/jsite/main/Configuration.java
src/main/java/de/todesbaum/jsite/main/Main.java
src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java
src/main/java/de/todesbaum/util/swing/TLabel.java
src/main/resources/de/todesbaum/jsite/i18n/jSite_it.properties [new file with mode: 0644]
src/main/resources/flag-it.png [new file with mode: 0644]
src/test/java/de/todesbaum/jsite/application/FileOptionTest.java [new file with mode: 0644]
src/test/java/de/todesbaum/jsite/application/Freenet7InterfaceTest.java [new file with mode: 0644]
src/test/java/de/todesbaum/jsite/application/ProjectTest.java [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 88db65b..2bc9a9c 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -4,19 +4,32 @@
        <artifactId>jSite</artifactId>
        <version>${version}</version>
        <properties>
-               <version>0.12</version>
+               <version>0.13</version>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.12.1</version>
+                       <version>0.13</version>
                </dependency>
                <dependency>
-                       <groupId>com.google.guava</groupId>
-                       <artifactId>guava</artifactId>
-                       <version>14.0.1</version>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+                       <version>4.12</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.hamcrest</groupId>
+                       <artifactId>hamcrest-library</artifactId>
+                       <version>1.3</version>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>org.mockito</groupId>
+                       <artifactId>mockito-core</artifactId>
+                       <version>1.10.19</version>
+                       <scope>test</scope>
                </dependency>
        </dependencies>
        <repositories>
@@ -31,8 +44,8 @@
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
-                                       <source>1.6</source>
-                                       <target>1.6</target>
+                                       <source>1.8</source>
+                                       <target>1.8</target>
                                        <encoding>UTF-8</encoding>
                                </configuration>
                        </plugin>
@@ -85,7 +98,7 @@
                        <plugin>
                                <groupId>org.jacoco</groupId>
                                <artifactId>jacoco-maven-plugin</artifactId>
-                               <version>0.7.1.201405082137</version>
+                               <version>0.7.5.201505241946</version>
                                <executions>
                                        <execution>
                                                <id>default-prepare-agent</id>
index 4c38286..8509257 100644 (file)
 
 package de.todesbaum.jsite.application;
 
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.of;
+import static java.util.Optional.empty;
 
-import com.google.common.base.Optional;
+import java.util.Optional;
 
 /**
  * Container for various file options.
@@ -64,7 +63,7 @@ public class FileOption {
        private String customKey;
 
        /** The changed name. */
-       private Optional<String> changedName = absent();
+       private Optional<String> changedName = empty();
 
        /** The default MIME type. */
        private final String defaultMimeType;
@@ -86,6 +85,20 @@ public class FileOption {
                mimeType = defaultMimeType;
        }
 
+       public FileOption(FileOption other) {
+               this.insert = other.insert;
+               this.forceInsert = other.forceInsert;
+               this.insertRedirect = other.insertRedirect;
+               this.lastInsertHash = other.lastInsertHash;
+               this.lastInsertEdition = other.lastInsertEdition;
+               this.lastInsertFilename = other.lastInsertFilename;
+               this.currentHash = other.currentHash;
+               this.customKey = other.customKey;
+               this.changedName = other.changedName;
+               this.defaultMimeType = other.defaultMimeType;
+               this.mimeType = other.mimeType;
+       }
+
        /**
         * Returns the custom key. The custom key is only used when
         * {@link #isInsert()} and {@link #isInsertRedirect()} both return {@code
@@ -297,7 +310,7 @@ public class FileOption {
         *            The new changed name for this file
         */
        public void setChangedName(String changedName) {
-               this.changedName = ((changedName != null) && (changedName.length() > 0)) ? of(changedName) : Optional.<String>absent();
+               this.changedName = ((changedName != null) && (changedName.length() > 0)) ? Optional.of(changedName) : Optional.<String>empty();
        }
 
        /**
index 657adfe..9263010 100644 (file)
@@ -21,6 +21,7 @@ package de.todesbaum.jsite.application;
 import java.io.IOException;
 
 import de.todesbaum.util.freenet.fcp2.Client;
+import de.todesbaum.util.freenet.fcp2.Command;
 import de.todesbaum.util.freenet.fcp2.Connection;
 import de.todesbaum.util.freenet.fcp2.GenerateSSK;
 import de.todesbaum.util.freenet.fcp2.Message;
@@ -39,35 +40,24 @@ public class Freenet7Interface {
        /** Counter. */
        private static int counter = 0;
 
+       private final NodeSupplier nodeSupplier;
+       private final ConnectionSupplier connectionSupplier;
+       private final ClientSupplier clientSupplier;
+
        /** 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++);
+       public Freenet7Interface() {
+               this(new DefaultNodeSupplier(), new DefaultConnectionSupplier(), new DefaultClientSupplier());
        }
 
-       /**
-        * 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++);
+       Freenet7Interface(NodeSupplier nodeSupplier, ConnectionSupplier connectionSupplier, ClientSupplier clientSupplier) {
+               this.nodeSupplier = nodeSupplier;
+               this.connectionSupplier = connectionSupplier;
+               this.clientSupplier = clientSupplier;
        }
 
        /**
@@ -78,8 +68,8 @@ public class Freenet7Interface {
         */
        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++);
+                       this.node = nodeSupplier.supply(node.getHostname(), node.getPort());
+                       connection = connectionSupplier.supply(node, "jSite-" + number + "-connection-" + counter++);
                } else {
                        this.node = null;
                        connection = null;
@@ -87,14 +77,6 @@ public class Freenet7Interface {
        }
 
        /**
-        * 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
@@ -111,7 +93,7 @@ public class Freenet7Interface {
         * @return The connection to the node
         */
        public Connection getConnection(String identifier) {
-               return new Connection(node, identifier);
+               return connectionSupplier.supply(node, identifier);
        }
 
        /**
@@ -144,7 +126,7 @@ public class Freenet7Interface {
                        throw new IOException("Node is offline.");
                }
                GenerateSSK generateSSK = new GenerateSSK();
-               Client client = new Client(connection, generateSSK);
+               Client client = clientSupplier.supply(connection, generateSSK);
                Message keypairMessage = client.readMessage();
                return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") };
        }
@@ -159,4 +141,49 @@ public class Freenet7Interface {
                return (node != null) && (connection != null);
        }
 
+       public interface NodeSupplier {
+
+               Node supply(String hostname, int port);
+
+       }
+
+       public static class DefaultNodeSupplier implements NodeSupplier {
+
+               @Override
+               public Node supply(String hostname, int port) {
+                       return new Node(hostname, port);
+               }
+
+       }
+
+       public interface ConnectionSupplier {
+
+               Connection supply(Node node, String identifier);
+
+       }
+
+       public static class DefaultConnectionSupplier implements ConnectionSupplier {
+
+               @Override
+               public Connection supply(Node node, String identifier) {
+                       return new Connection(node, identifier);
+               }
+
+       }
+
+       public interface ClientSupplier {
+
+               Client supply(Connection connection, Command command) throws IOException;
+
+       }
+
+       public static class DefaultClientSupplier implements ClientSupplier {
+
+               @Override
+               public Client supply(Connection connection, Command command) throws IOException {
+                       return new Client(connection, command);
+               }
+
+       }
+
 }
index 836a2cd..59dcba9 100644 (file)
 package de.todesbaum.jsite.application;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -94,7 +96,9 @@ public class Project implements Comparable<Project> {
                lastInsertionTime = project.lastInsertionTime;
                alwaysForceInserts = project.alwaysForceInserts;
                ignoreHiddenFiles = project.ignoreHiddenFiles;
-               fileOptions = new HashMap<String, FileOption>(project.fileOptions);
+               for (Entry<String, FileOption> fileOption : fileOptions.entrySet()) {
+                       fileOptions.put(fileOption.getKey(), new FileOption(fileOption.getValue()));
+               }
        }
 
        /**
@@ -363,13 +367,33 @@ public class Project implements Comparable<Project> {
         */
        public FileOption getFileOption(String filename) {
                FileOption fileOption = fileOptions.get(filename);
+               String defaultMimeType = "application/octet-stream";
                if (fileOption == null) {
-                       fileOption = new FileOption(MimeTypes.getMimeType(filename.substring(filename.lastIndexOf('.') + 1)));
-                       fileOptions.put(filename, fileOption);
+                       List<String> suffixes = getSuffixes(filename);
+                       for (String suffix : suffixes) {
+                               String mimeType = MimeTypes.getMimeType(suffix);
+                               if (!mimeType.equals(defaultMimeType)) {
+                                       defaultMimeType = mimeType;
+                                       break;
+                               }
+                       }
+                       fileOption = new FileOption(defaultMimeType);
                }
+               fileOptions.put(filename, fileOption);
                return fileOption;
        }
 
+       private List<String> getSuffixes(String filename) {
+               List<String> suffixes = new ArrayList<>();
+               int dot = filename.lastIndexOf(".");
+               while (dot > -1) {
+                       String suffix = filename.substring(dot + 1);
+                       suffixes.add(0, suffix);
+                       dot = filename.lastIndexOf(".", dot - 1);
+               }
+               return suffixes;
+       }
+
        /**
         * Sets options for a file.
         *
@@ -460,7 +484,7 @@ public class Project implements Comparable<Project> {
                        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.getChangedName().or(fileOptionEntry.getKey()));
+                               fileOption.setLastInsertFilename(fileOption.getChangedName().orElse(fileOptionEntry.getKey()));
                        }
                        fileOption.setForceInsert(false);
                }
index 35a2452..c7dad3b 100644 (file)
@@ -25,25 +25,26 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import net.pterodactylus.util.io.StreamCopier.ProgressListener;
 
-import com.google.common.base.Optional;
 import de.todesbaum.jsite.gui.FileScanner;
-import de.todesbaum.jsite.gui.FileScanner.ScannedFile;
+import de.todesbaum.jsite.gui.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;
@@ -66,7 +67,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
        private static final int random = (int) (Math.random() * Integer.MAX_VALUE);
 
        /** Counter for FCP connection identifier. */
-       private static int counter = 0;
+       private static final AtomicInteger counter = new AtomicInteger();
 
        private final ProjectInsertListeners projectInsertListeners = new ProjectInsertListeners();
 
@@ -100,9 +101,6 @@ public class ProjectInserter implements FileScannerListener, Runnable {
        /** The insert priority. */
        private PriorityClass priority;
 
-       /** The manifest putter. */
-       private ManifestPutter manifestPutter;
-
        /**
         * Adds a listener to the list of registered listeners.
         *
@@ -166,16 +164,6 @@ public class ProjectInserter implements FileScannerListener, Runnable {
        }
 
        /**
-        * 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
@@ -184,9 +172,8 @@ public class ProjectInserter implements FileScannerListener, Runnable {
        public void start(ProgressListener progressListener) {
                cancelled = false;
                this.progressListener = progressListener;
-               fileScanner = new FileScanner(project);
-               fileScanner.addFileScannerListener(this);
-               new Thread(fileScanner).start();
+               fileScanner = new FileScanner(project, this);
+               fileScanner.startInBackground();
        }
 
        /**
@@ -209,7 +196,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
         *              The name and hash of the file to insert
         * @return A file entry for the given file
         */
-       private FileEntry createFileEntry(ScannedFile file) {
+       private Optional<FileEntry> createFileEntry(ScannedFile file) {
                String filename = file.getFilename();
                FileOption fileOption = project.getFileOption(filename);
                if (fileOption.isInsert()) {
@@ -218,25 +205,25 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                        if (!project.isAlwaysForceInsert() && !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.getChangedName().or(filename), fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename());
+                               return Optional.of(new RedirectFileEntry(fileOption.getChangedName().orElse(filename), fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename()));
                        }
                        try {
-                               return createFileEntry(filename, fileOption.getChangedName(), fileOption.getMimeType());
+                               return Optional.of(createFileEntry(filename, fileOption.getChangedName(), fileOption.getMimeType()));
                        } catch (IOException ioe1) {
                                /* ignore, null is returned. */
                        }
                } else {
                        if (fileOption.isInsertRedirect()) {
-                               return new RedirectFileEntry(fileOption.getChangedName().or(filename), fileOption.getMimeType(), fileOption.getCustomKey());
+                               return Optional.of(new RedirectFileEntry(fileOption.getChangedName().orElse(filename), fileOption.getMimeType(), fileOption.getCustomKey()));
                        }
                }
-               return null;
+               return Optional.empty();
        }
 
        private FileEntry createFileEntry(String filename, Optional<String> changedName, String mimeType) throws FileNotFoundException {
                File physicalFile = new File(project.getLocalPath(), filename);
                InputStream fileEntryInputStream = new FileInputStream(physicalFile);
-               return new DirectFileEntry(changedName.or(filename), mimeType, fileEntryInputStream, physicalFile.length());
+               return new DirectFileEntry(changedName.orElse(filename), mimeType, fileEntryInputStream, physicalFile.length());
        }
 
        /**
@@ -294,23 +281,16 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                                logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey());
                                continue;
                        }
-                       String fileName = fileOption.getChangedName().or(fileOptionEntry.getKey());
+                       String fileName = fileOption.getChangedName().orElse(fileOptionEntry.getKey());
                        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();
+               FileScanner fileScanner = new FileScanner(project, (error, files) -> completionLatch.countDown());
+               fileScanner.startInBackground();
                while (completionLatch.getCount() > 0) {
                        try {
                                completionLatch.await();
@@ -342,7 +322,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
 
                /* create connection to node */
                synchronized (lockObject) {
-                       connection = freenetInterface.getConnection("project-insert-" + random + counter++);
+                       connection = freenetInterface.getConnection("project-insert-" + random + counter.getAndIncrement());
                }
                connection.setTempDirectory(tempDirectory);
                boolean connected = false;
@@ -363,7 +343,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                /* collect files */
                int edition = project.getEdition();
                String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/";
-               ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI, tempDirectory);
+               ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter.getAndIncrement(), dirURI, tempDirectory);
                if ((project.getIndexFile() != null) && (project.getIndexFile().length() > 0)) {
                        FileOption indexFileOption = project.getFileOption(project.getIndexFile());
                        Optional<String> changedName = indexFileOption.getChangedName();
@@ -377,12 +357,11 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                putDir.setMaxRetries(-1);
                putDir.setEarlyEncode(useEarlyEncode);
                putDir.setPriorityClass(priority);
-               putDir.setManifestPutter(manifestPutter);
                for (ScannedFile file : files) {
-                       FileEntry fileEntry = createFileEntry(file);
-                       if (fileEntry != null) {
+                       Optional<FileEntry> fileEntry = createFileEntry(file);
+                       if (fileEntry.isPresent()) {
                                try {
-                                       putDir.addFileEntry(fileEntry);
+                                       putDir.addFileEntry(fileEntry.get());
                                } catch (IOException ioe1) {
                                        projectInsertListeners.fireProjectInsertFinished(project, false, ioe1);
                                        return;
@@ -448,13 +427,12 @@ public class ProjectInserter implements FileScannerListener, Runnable {
         * {@inheritDoc}
         */
        @Override
-       public void fileScannerFinished(FileScanner fileScanner) {
-               if (!fileScanner.isError()) {
+       public void fileScannerFinished(boolean error, Collection<ScannedFile> files) {
+               if (!error) {
                        new Thread(this).start();
                } else {
                        projectInsertListeners.fireProjectInsertFinished(project, false, null);
                }
-               fileScanner.removeFileScannerListener(this);
        }
 
        /**
index 24949a9..4e95b73 100644 (file)
@@ -27,7 +27,6 @@ import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import net.pterodactylus.util.io.Closer;
-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;
@@ -51,7 +50,7 @@ public class UpdateChecker implements Runnable {
        private static int counter = 0;
 
        /** The edition for the update check URL. */
-       private static final int UPDATE_EDITION = 7;
+       private static final int UPDATE_EDITION = 11;
 
        /** The URL for update checks. */
        private static final String UPDATE_KEY = "USK@1waTsw46L9-JEQ8yX1khjkfHcn--g0MlMsTlYHax9zQ,oYyxr5jyFnaTsVGDQWk9e3ddOWGKnqEASxAk08MHT2Y,AQACAAE";
@@ -69,7 +68,7 @@ public class UpdateChecker implements Runnable {
        private int lastUpdateEdition = UPDATE_EDITION;
 
        /** Last found version. */
-       private Version lastVersion = Main.getVersion();
+       private Version lastVersion;
 
        /** The freenet interface. */
        private final Freenet7Interface freenetInterface;
@@ -81,8 +80,9 @@ public class UpdateChecker implements Runnable {
         * @param freenetInterface
         *            The freenet interface
         */
-       public UpdateChecker(Freenet7Interface freenetInterface) {
+       public UpdateChecker(Freenet7Interface freenetInterface, Version currentVersion) {
                this.freenetInterface = freenetInterface;
+               this.lastVersion = currentVersion;
        }
 
        //
index eef5158..04705b5 100644 (file)
@@ -29,6 +29,7 @@ import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -43,7 +44,7 @@ import de.todesbaum.jsite.i18n.I18n;
  * files as an event.
  *
  * @see Project#getLocalPath()
- * @see FileScannerListener#fileScannerFinished(FileScanner)
+ * @see FileScannerListener#fileScannerFinished(boolean, java.util.Collection)
  * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
  */
 public class FileScanner implements Runnable {
@@ -52,7 +53,7 @@ public class FileScanner implements Runnable {
        private final static Logger logger = Logger.getLogger(FileScanner.class.getName());
 
        /** The list of listeners. */
-       private final List<FileScannerListener> fileScannerListeners = new ArrayList<FileScannerListener>();
+       private final FileScannerListener fileScannerListener;
 
        /** The project to scan. */
        private final Project project;
@@ -72,37 +73,9 @@ public class FileScanner implements Runnable {
         * @param project
         *            The project whose files to scan
         */
-       public FileScanner(Project project) {
+       public FileScanner(Project project, FileScannerListener fileScannerListener) {
                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);
-               }
+               this.fileScannerListener = Objects.requireNonNull(fileScannerListener);
        }
 
        /**
@@ -115,13 +88,17 @@ public class FileScanner implements Runnable {
                return lastFilename;
        }
 
+       public void startInBackground() {
+               new Thread(this).start();
+       }
+
        /**
         * {@inheritDoc}
         * <p>
         * Scans all available files in the project’s local path and emits an event
         * when finished.
         *
-        * @see FileScannerListener#fileScannerFinished(FileScanner)
+        * @see FileScannerListener#fileScannerFinished(boolean, java.util.Collection)
         */
        @Override
        public void run() {
@@ -134,7 +111,7 @@ public class FileScanner implements Runnable {
                } catch (IOException ioe1) {
                        error = true;
                }
-               fireFileScannerFinished();
+               fileScannerListener.fileScannerFinished(error, files);
        }
 
        /**
@@ -234,95 +211,4 @@ public class FileScanner implements Runnable {
                return hexString.toString();
        }
 
-       /**
-        * 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}
-                */
-               @Override
-               public int compareTo(ScannedFile scannedFile) {
-                       return filename.compareTo(scannedFile.filename);
-               }
-
-       }
-
 }
index d3d5145..4088697 100644 (file)
@@ -18,6 +18,7 @@
 
 package de.todesbaum.jsite.gui;
 
+import java.util.Collection;
 import java.util.EventListener;
 
 /**
@@ -29,12 +30,6 @@ import java.util.EventListener;
  */
 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);
+       void fileScannerFinished(boolean error, Collection<ScannedFile> files);
 
-}
\ No newline at end of file
+}
index 6f2c38e..9797708 100644 (file)
@@ -348,7 +348,9 @@ public class KeyDialog extends JDialog {
                final JLabel projectLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.project"));
                contentPanel.add(projectLabel, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0));
 
-               projectsComboBox = new JComboBox(new ComboBoxModelList<Project>(projects));
+               synchronized (projects) {
+                       projectsComboBox = new JComboBox(new ComboBoxModelList<Project>(projects));
+               }
                projectsComboBox.addActionListener(new ActionListener() {
 
                        @Override
index 7cbeec6..5c4dcfa 100644 (file)
@@ -40,7 +40,6 @@ 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;
@@ -112,9 +111,6 @@ public class PreferencesPage extends TWizardPage {
        /** The insert priority select box. */
        private JComboBox insertPriorityComboBox;
 
-       /** The manifest putter select box. */
-       private JComboBox manifestPutterComboBox;
-
        /**
         * Creates a new “preferences” page.
         *
@@ -266,25 +262,6 @@ public class PreferencesPage extends TWizardPage {
        }
 
        /**
-        * 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
@@ -466,12 +443,6 @@ public class PreferencesPage extends TWizardPage {
                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() {
 
                        /**
@@ -483,7 +454,6 @@ public class PreferencesPage extends TWizardPage {
                                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"));
                        }
                });
 
index 0e700c9..4229e48 100644 (file)
@@ -29,9 +29,9 @@ import java.awt.event.KeyEvent;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.text.MessageFormat;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 
 import javax.swing.AbstractAction;
@@ -65,7 +65,6 @@ import net.pterodactylus.util.swing.SwingUtils;
 import net.pterodactylus.util.thread.StoppableDelay;
 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.swing.TLabel;
@@ -182,8 +181,7 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis
        @Override
        public void pageAdded(TWizard wizard) {
                /* create file scanner. */
-               fileScanner = new FileScanner(project);
-               fileScanner.addFileScannerListener(this);
+               fileScanner = new FileScanner(project, this);
 
                actionScan();
                this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous"));
@@ -338,7 +336,7 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis
                scanningFilesDialog.getContentPane().add(progressPanel, BorderLayout.CENTER);
                progressPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
 
-               final TLabel scanningLabel = new TLabel(I18n.getMessage("jsite.project-files.scanning"), SwingConstants.CENTER);
+               final JLabel scanningLabel = new JLabel(I18n.getMessage("jsite.project-files.scanning"), SwingConstants.CENTER);
                progressPanel.add(scanningLabel, BorderLayout.NORTH);
                progressBar = new JProgressBar(SwingConstants.HORIZONTAL);
                progressPanel.add(progressBar, BorderLayout.SOUTH);
@@ -445,7 +443,7 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis
                                scanningFilesDialog.setVisible(false);
                        }
                }, 2000);
-               new Thread(fileScanner).start();
+               fileScanner.startInBackground();
                new Thread(delayedNotification).start();
                new Thread(new Runnable() {
 
@@ -470,11 +468,9 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis
         * Updates the file list.
         */
        @Override
-       public void fileScannerFinished(FileScanner fileScanner) {
+       public void fileScannerFinished(boolean error, Collection<ScannedFile> files) {
                delayedNotification.finish();
-               final boolean error = fileScanner.isError();
                if (!error) {
-                       final List<ScannedFile> files = fileScanner.getFiles();
                        SwingUtilities.invokeLater(new Runnable() {
 
                                @Override
@@ -630,7 +626,7 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis
                        fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey());
                        fileOptionsRenameCheckBox.setSelected(fileOption.getChangedName().isPresent());
                        fileOptionsRenameTextField.setEnabled(fileOption.getChangedName().isPresent());
-                       fileOptionsRenameTextField.setText(fileOption.getChangedName().or(""));
+                       fileOptionsRenameTextField.setText(fileOption.getChangedName().orElse(""));
                        fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType());
                } else {
                        defaultFileCheckBox.setSelected(false);
index f68144d..fd793ce 100644 (file)
@@ -55,7 +55,6 @@ 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.swing.TWizard;
 import de.todesbaum.util.swing.TWizardPage;
@@ -342,17 +341,6 @@ public class ProjectInsertPage extends TWizardPage implements InsertListener, Cl
                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
        //
diff --git a/src/main/java/de/todesbaum/jsite/gui/ScannedFile.java b/src/main/java/de/todesbaum/jsite/gui/ScannedFile.java
new file mode 100644 (file)
index 0000000..00a659e
--- /dev/null
@@ -0,0 +1,92 @@
+package de.todesbaum.jsite.gui;
+
+/**
+ * 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 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}
+        */
+       @Override
+       public int compareTo(ScannedFile scannedFile) {
+               return filename.compareTo(scannedFile.filename);
+       }
+
+}
index 3280551..1765074 100644 (file)
@@ -272,6 +272,9 @@ public class CLI implements InsertListener {
         */
        @Override
        public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized) {
+               if (total == 0) {
+                       return;
+               }
                outputWriter.println("Progress: " + succeeded + " done, " + failed + " failed, " + fatal + " fatal, " + total + " total" + (finalized ? " (finalized)" : "") + ", " + ((succeeded + failed + fatal) * 100 / total) + "%");
        }
 
index 2a27e89..41eafdb 100644 (file)
@@ -43,8 +43,8 @@ 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 org.w3c.dom.Document;
 
 /**
  * The configuration.
@@ -129,7 +129,10 @@ public class Configuration {
                                        StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length());
                                        fileByteOutputStream.close();
                                        byte[] fileBytes = fileByteOutputStream.toByteArray();
-                                       rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes));
+                                       Document document = XML.transformToDocument(fileBytes);
+                                       if (document != null) {
+                                               rootNode = SimpleXML.fromDocument(document);
+                                       }
                                        return;
                                } catch (FileNotFoundException e) {
                                        /* ignore. */
@@ -430,7 +433,7 @@ public class Configuration {
                                        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().orNull());
+                                       fileOptionNode.append("changed-name", fileOption.getChangedName().orElse(null));
                                        fileOptionNode.append("mime-type", fileOption.getMimeType());
                                }
                        }
@@ -623,25 +626,4 @@ public class Configuration {
                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;
-       }
-
 }
index 59a3116..8a0b898 100644 (file)
@@ -80,7 +80,7 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
        private static final Logger logger = Logger.getLogger(Main.class.getName());
 
        /** The version. */
-       private static final Version VERSION = new Version(0, 12);
+       private static final Version VERSION = new Version(0, 13);
 
        /** The configuration. */
        private Configuration configuration;
@@ -126,6 +126,7 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
                        Locale.ENGLISH,
                        Locale.GERMAN,
                        Locale.FRENCH,
+                       Locale.ITALIAN,
                        new Locale("pl"),
                        new Locale("fi")
        };
@@ -196,7 +197,7 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
                jSiteIcon = IconLoader.loadIcon("/jsite-icon.png");
                wizard.setIcon(jSiteIcon);
 
-               updateChecker = new UpdateChecker(freenetInterface);
+               updateChecker = new UpdateChecker(freenetInterface, getVersion());
                updateChecker.addUpdateListener(this);
                updateChecker.start();
 
@@ -483,7 +484,6 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
                ((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);
@@ -588,7 +588,6 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
                        projectInsertPage.setTempDirectory(tempDirectory);
                        projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode());
                        projectInsertPage.setPriority(configuration.getPriority());
-                       projectInsertPage.setManifestPutter(configuration.getManifestPutter());
                        projectInsertPage.startInsert();
                        nodeMenu.setEnabled(false);
                        optionsPreferencesAction.setEnabled(false);
@@ -607,7 +606,6 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen
                        optionsPreferencesAction.setEnabled(true);
                        configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode());
                        configuration.setPriority(preferencesPage.getPriority());
-                       configuration.setManifestPutter(preferencesPage.getManifestPutter());
                        configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation());
                }
        }
index 386fe1d..4ea4b1d 100644 (file)
@@ -30,66 +30,9 @@ import java.io.Writer;
  */
 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.
         *
@@ -128,30 +71,6 @@ public class ClientPutDir<C extends ClientPutDir<?>> extends ClientPut {
        }
 
        /**
-        * 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
@@ -159,9 +78,6 @@ public class ClientPutDir<C extends ClientPutDir<?>> extends ClientPut {
                super.write(writer);
                if (defaultName != null)
                        writer.write("DefaultName=" + defaultName + LINEFEED);
-               if (manifestPutter != null) {
-                       writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED);
-               }
        }
 
 }
index 0393f5c..822ca17 100644 (file)
@@ -29,40 +29,6 @@ import javax.swing.JLabel;
  */
 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);
@@ -70,24 +36,4 @@ public class TLabel extends JLabel {
                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/resources/de/todesbaum/jsite/i18n/jSite_it.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite_it.properties
new file mode 100644 (file)
index 0000000..1fc66b6
--- /dev/null
@@ -0,0 +1,207 @@
+#
+# jSite - jSite_it.properties - Copyright © 2006–2014 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.
+#
+
+# Italian language file by Stefano Calabrese <statix1@hotmail.it>
+
+# 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=Annulla
+
+jsite.wizard.previous=Precedente
+jsite.wizard.next=Prossimo
+jsite.wizard.quit=Esci
+
+jsite.quit.question=Vuoi veramente uscire?
+jsite.quit.question.title=Confermi l\u2019uscita?
+jsite.quit.overwrite-configuration=<html><b>Sovrascrivere configurazione?</b><br><br>Esiste gi\u00e0 un file di configurazione:<br><code>{0}</code><br><br>Dovrebbe essere sovrascritto?</html>
+jsite.quit.overwrite-configuration.title=Sovrascrivere configurazione?
+jsite.quit.config-not-saved=<html><b>Configuratione non salvata</b><br><br>Non \u00e8 stato possibile salvare la configurazione.<br>Vuoi uscire comunque?</html>
+
+jsite.menu.languages=Lingue
+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.language.fi=Suomi
+jsite.menu.nodes=Nodi
+jsite.menu.nodes.manage-nodes=Gestisci nodi
+jsite.menu.options=Opzioni
+jsite.menu.options.preferences=Preferenze
+jsite.menu.help=Aiuto
+jsite.menu.help.check-for-updates=Cerca aggiornamenti
+jsite.menu.help.about=Informazioni
+
+jsite.about.message=<html><big><b>jSite {0}</b></big><br><br>Copyright \u00a9 2006\u20132012 David Roden<br>Rilasciato sotto la GNU General Public License</html>
+
+jsite.node-manager.heading=Gestore Nodi
+jsite.node-manager.description=Gestisci i tuoi nodi qui.
+jsite.node-manager.node-information=Informazioni Nodo
+jsite.node-manager.add-node=Aggiungi Nodo
+jsite.node-manager.new-node=Nuovo Nodo
+jsite.node-manager.delete-node=Elimina Nodo
+jsite.node-manager.delete-node.warning=<html><b>Conferma l\u2019eliminazione del nodo</b><br><br>Vuoi davvero cancellare questo nodo?</html>
+jsite.node-manager.name=Nome
+jsite.node-manager.hostname=Hostname
+jsite.node-manager.port=Porta
+
+jsite.preferences.heading=Preferenze
+jsite.preferences.description=Usa questa pagina per gestire alcune impostazioni globali.
+jsite.preferences.temp-directory=Cartella per i file temporanei
+jsite.preferences.temp-directory.default=Predefinita (scelta dal sistema)
+jsite.preferences.temp-directory.custom=Personalizza
+jsite.preferences.temp-directory.choose=Scegli
+jsite.preferences.temp-directory.choose.approve=Scegli
+jsite.preferences.config-directory=Posizione del file di configurazione
+jsite.preferences.config-directory.jar=Accanto al file JAR
+jsite.preferences.config-directory.home=Cartella principale
+jsite.preferences.config-directory.custom=Cartella personalizzata
+jsite.preferences.insert-options=Opzioni d\u2019inserimento
+jsite.preferences.insert-options.use-early-encode=Genera l\u2019URI finale in anticipo
+jsite.preferences.insert-options.priority=Priorit\u00e0
+jsite.preferences.insert-options.manifest-putter=Aggiungi manifesto
+
+jsite.insert.heading=Inserimento Progetto
+jsite.insert.description=Per favore attendi mentre il progetto viene inserito.
+jsite.insert.project-information=Informazioni Progetto
+jsite.insert.request-uri=Freesite
+jsite.insert.start-time=Tempo d\u2019inizio
+jsite.insert.starting=Inizio\u2026
+jsite.insert.done=Fatto.
+jsite.insert.done.title=Inserimento completato
+jsite.insert.insert-aborted=L\u2019inserimento \u00e8 stato annullato.
+jsite.insert.insert-aborted.title=Inserimento Annullato
+jsite.insert.progress=Progresso
+jsite.insert.k-per-s=KB/s
+jsite.insert.insert-failed=<html><b>Inserimento fallito</b><br><br>L\u2019inserimento del progetto \u00e8 fallito.<br>Non \u00e8 stato possibile inserire alcuni file.</html>
+jsite.insert.insert-failed-with-cause=<html><b>Inserimento fallito</b><br><br>L\u2019inserimento del progetto \u00e8 fallito.<br>Non \u00e8 stato possibile inserire alcuni file.<br>Si \u00e8 verificato il seguente errore:<br><br><code>{0}</code></html>
+jsite.insert.insert-failed.title=Inserimento fallito
+jsite.insert.inserted=<html><b>Progetto inserito</b><br><br>Il tuo progetto \u00e8 stato inserito con successo.</html>
+jsite.insert.okay-copy-uri=Copia URI alla Clipboard
+jsite.insert.reinserted-edition=<html><b>Edizione Reinserita</b><br><br>L\u2019edizione che stai inserendo<br>\u00e8 gi\u00e0 stata inserita prima.</html>
+jsite.insert.reinserted-edition.title=Edizione Reinserita
+
+jsite.file-scanner.can-not-read-directory=Impossibile aprire cartella
+
+jsite.project.heading=Seleziona un Progetto
+jsite.project.description=Seleziona un progetto da esaminare dalla seguente lista, oppure crea un nuovo progetto.
+jsite.project.action.browse=Sfoglia
+jsite.project.action.browse.choose=Scegli
+jsite.project.action.browse.tooltip=Sfoglia per cartella
+jsite.project.action.add-project=Aggiungi progetto
+jsite.project.action.add-project.tooltip=Aggiungi nuovo progetto
+jsite.project.new-project.name=Nuovo Progetto
+jsite.project.action.delete-project=Elimina progetto
+jsite.project.action.delete-project.tooltip=Cancella un progetto
+jsite.project.action.delete-project.confirm=<html><b>Conferma eliminzione</b><br><br>Il progetto \u201c{0}\u201d sar\u00e0 cancellato!<br>Vuoi continuare?</html>
+jsite.project.action.clone-project=Clona progetto
+jsite.project.action.clone-project.copy=Copia di {0}
+jsite.project.action.clone-project.tooltip=Clona il progetto selezionato
+jsite.project.action.copy-uri=Copia URI sulla Clipboard
+jsite.project.action.copy-uri.tooltip=Copia l\u2019URI del progetto sulla clipboard
+jsite.project.action.manage-keys=Gestisci Chiavi
+jsite.project.action.manage-keys.tooltip=Gestisce le chiavi di questo progetto
+jsite.project.action.reset-edition=Resetta Edizione
+jsite.project.action.reset-edition.tooltip=Resetta il numero di edizione del progetto
+jsite.project.project.information=Informazioni Progetto
+jsite.project.project.name=Nome
+jsite.project.project.description=Descrizione
+jsite.project.project.local-path=Percorso locale
+jsite.project.project.address=Indirizzo
+jsite.project.project.path=Percorso Freesite
+jsite.project.project.edition=Edizione
+jsite.project.project.uri=URI
+jsite.project.keygen.io-error=<html><b>Comunicazione nodo fallita</b><br><br>La comunicazione con il nodo \u00e8 fallita<br>con il seguente messaggio d\u2019errore:<br><br><code>{0}</code><br><br>Per favore, assicurati d\u2019aver inserito<br>l\u2019hostname ed il numero della porta corretti<br>sulla pagina \u201cGestore Nodi\u201d.</html>
+jsite.project.warning.generate-new-key=<html><b>Generare nuova chiave?</b><br><br>Se generi una nuova chiave, il tuo sito sar\u00e0 pubblicato<br>sotto quella nuova chiave. Qualsiasi affidamento gli altri utenti abbiano fatto<br>sulla vecchia chiave del tuo sito sparir\u00e0!<br>Inoltre, l\u2019edizione verr\u00e0 resettata.</html>
+jsite.project.warning.reset-edition=<html><b>Resettare edizione?</b><br><br>Resettare l\u2019edizione pu\u00f2 portare ad inserimenti fallimentari<br>e molta confusione se non hai cambiato<br>il percorso o le chiavi del progetto!</html>
+jsite.project.warning.use-clipboard-now=<html><b>URI copiato</b><br><br>Per piacere, considera il fatto che uscire da jSite adesso<br>potrebbe svuotare la clipboard. Sei pregato di usare immediatamente l\u2019URI<br>copiato in un\u2019altra finestra!</html>
+
+jsite.project-files.heading=File del Progetto
+jsite.project-files.description=<html>Su questa pagina puoi specificare i parametri per i file contenuti nel progetto, come le<br>chiavi generate esternamente o i tipi MIME, se il rilevamento automatico \u00e8 fallito.</html>
+jsite.project-files.action.rescan=Scansiona di nuovo
+jsite.project-files.action.rescan.tooltip=Scansiona di nuovo la cartella del progetto per i nuovi file
+jsite.project-files.always-force-insert=Forza sempre l\u2019inserimento
+jsite.project-files.always-force-insert.tooltip=Quando selezionato, tutti i file di questo progetto sono inseriti anche se non sono cambiati
+jsite.project-files.ignore-hidden-files=Ignora file nascosti
+jsite.project-files.ignore-hidden-files.tooltip=Se selezionato, i file nascosti non vengono inseriti
+jsite.project-files.file-options=Opzioni File
+jsite.project-files.default=File predefinito
+jsite.project-files.default.tooltip=Specifica che questo file \u00e8 il file indice del progetto
+jsite.project-files.insert=Inserisci
+jsite.project-files.insert.tooltip=Non spuntarlo se non vuoi inserire questo file
+jsite.project-files.force-insert=Forza inserimento
+jsite.project-files.force-insert.tooltip=Forza l\u2019inserimento di questo file anche se non \u00e8 modificato
+jsite.project-files.insert-redirect=Redirect
+jsite.project-files.insert-redirect.tooltip=Seleziona se vuoi inserire un redirect per questo file
+jsite.project-files.custom-key=Chiave personalizzata
+jsite.project-files.custom-key.tooltip=La chiave creata esternamente per questo file
+jsite.project-files.rename=Rinomina
+jsite.project-files.rename.tooltip=Rinomina il file nel sito caricato
+jsite.project-files.mime-type=Tipo MIME
+jsite.project-files.mime-type.tooltip=Seleziona il tipo MIME corretto qui se il rilevamento \u00e8 fallito
+jsite.project-files.container=Contenitore
+jsite.project-files.container.tooltip=Seleziona un contenitore per il file corrente
+jsite.project-files.scan-error=<html><b>Errore scansione file</b><br><br>O la cartella del progetto non esiste<br>oppure alcuni file/cartelle dentro di esso non sono accessibili.<br>Per favore, torna indietro e seleziona la cartella corretta.</html>
+jsite.project-files.insert-now=Inserisci adesso
+jsite.project-files.invalid-default-file=Solo i file nella cartella principale possono essere selezionati come file predefiniti.
+jsite.project-files.scanning=Scansionando\u2026
+
+jsite.update-checker.found-version.title=Trovata nuova versione
+jsite.update-checker.found-version.message=<html>Una nuova versione \u00e8 stata trovata.<br><br>Versione {0} (rilasciata {1,date})</html>
+jsite.update-checker.latest-version.title=Controlla aggiornamenti
+jsite.update-checker.latest-version.newer.message=<html>Stai usando la versione {0} ma<br>\u00e8 stata stata trovata la versione pi\u00f9 recente ({1})!</html>
+jsite.update-checker.latest-version.older.message=<html>Stai usando la versione {0} ma<br>l\u2019ultima versione \u00e8 {1}.</html>
+jsite.update-checker.latest-version.okay.message=<html>Attualmente stai usando la versione {0}<br>che \u00e8 l\u2019ultima uscita.</html>
+
+jsite.key-dialog.title=Gestisci Chiavi del Progetto
+jsite.key-dialog.button.ok.tooltip=Conferma i cambiamenti
+jsite.key-dialog.button.cancel.tooltip=Annulla i cambiamenti
+jsite.key-dialog.button.copy-from-project=Copia da Progetto
+jsite.key-dialog.button.copy-from-project.tooltip=Copia le chiavi pubbliche e private dal progetto selezionato
+jsite.key-dialog.button.copy-from-identity=Copia da Identit\u00e0
+jsite.key-dialog.button.copy-from-identity.tooltip=Copia le chiavi pubbliche e private dall\u2019identit\u00e0 selezionata
+jsite.key-dialog.button.generate=Rigenera Chiavi
+jsite.key-dialog.button.generate.tooltip=Crea una nuova coppia di chiavi
+jsite.key-dialog.label.keys=<html><b>Chiavi</b></html>
+jsite.key-dialog.label.private-key=Chiave Privata
+jsite.key-dialog.label.public-key=Chiave Pubblica
+jsite.key-dialog.label.copy-keys=<html><b>Copia Chiavi</b></html>
+jsite.key-dialog.label.project=Progetto
+jsite.key-dialog.label.identity=Identit\u00e0
+jsite.key-dialog.label.actions=<html><b>Azioni</b></html>
+
+jsite.warning.empty-index=<html><b>Nessun file predefinito</b><br><br>Non hai specificato un file predefinito per questo progetto.<br>Nonostante sia possibile inserire un progetto senza un file<br>predefinito, dovresti comunque specificarne uno per facilitare la navigazione.</html>
+jsite.warning.index-not-html=<html><b>Il tuo file predefinito non \u00e8 HTML</b><br><br>Il tuo file predefinito non ha il tipo MIME \u201ctext/html\u201d!<br>Caricare il tuo Freesite su un browser potrebbe dare risultati inaspettati.</html>
+jsite.warning.site-larger-than-2-mib=<html><b>Il sito \u00e8 pi\u00f9 grande di 2 MiB!</b><br><br>Il tuo sito contiene pi\u00f9 di 2 megabyte di dati.<br>A causa di alcuni bug su Freenet, esso probabilmente non verr\u00e0 caricato correttamente.<br>Prova a ridurre le dimensioni del tuo sito, oppure continua a tuo rischio.</html>
+
+jsite.error.no-node-selected=<html><b>Nessun nodo selezionato</b><br><br>Per favore, seleziona un nodo dal menu!</html>
+jsite.error.no-node-running=<html><b>Il nodo non funziona</b><br><br>Non puoi inserire un progetto se il tuo nodo non funziona.<br>Per favore, attiva il nodo e riprova.</html>
+jsite.error.no-local-path=<html><b>Nessun percorso locale</b><br><br>Non hai specificato un percorso locale per i file da inserire.<br>Non \u00e8 possibile continuare senza.</html>
+jsite.error.no-path=<html><b>Nessun percorso freesite</b><br><br>Non hai specificato un percorso freesite.<br>Non \u00e8 possibile continuare senza.</html>
+jsite.error.index-missing=<html>File predefinito mancante</b><br><br>Un file predefinito era stato specificato in precedenza ma<br>non esiste pi\u00f9! Per favore, seleziona<br>un nuovo file predefinito dalla lista dei file.</html>
+jsite.error.index-not-inserted=<html><b>File predefinito non inserito</b><br><br>Hai scelto di non inserire il file predefinito!<br>Devi scegliere di inserirlo oppure selezionare<br>un file predefinito diverso!</html>
+jsite.error.no-custom-key=<html><b>Nessuna chiave personalizzata per il file</b><br><br>Hai specificato di non inserire <code>{0}</code><br> ma non hai inserito una chiave per reindirizzarlo!</html>
+jsite.error.no-files-to-insert=<html><b>Nessun file da inserire</b><br><br>Non hai selezionato alcun file da inserire!<br>Per favore seleziona almeno un file da inserire.</html>
+jsite.error.duplicate-file=<html><b>File duplicato</b><br><br>Il file <code>{0}</code> \u00e8 inserito due volte!<br>Per favore controlla i tuoi nomi file ed i redirect.</html>
diff --git a/src/main/resources/flag-it.png b/src/main/resources/flag-it.png
new file mode 100644 (file)
index 0000000..9de468b
Binary files /dev/null and b/src/main/resources/flag-it.png differ
diff --git a/src/test/java/de/todesbaum/jsite/application/FileOptionTest.java b/src/test/java/de/todesbaum/jsite/application/FileOptionTest.java
new file mode 100644 (file)
index 0000000..880b6e6
--- /dev/null
@@ -0,0 +1,239 @@
+package de.todesbaum.jsite.application;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import java.util.Optional;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FileOption}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FileOptionTest {
+
+       private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
+       private static final String DEFAULT_CUSTOM_KEY = "CHK@";
+       private static final String CUSTOM_KEY = "KSK@custom-key";
+       private static final String EMPTY_CUSTOM_KEY = "";
+       private static final boolean DEFAULT_INSERT = true;
+       private static final boolean CUSTOM_INSERT = false;
+       private static final boolean DEFAULT_FORCE_INSERT = false;
+       private static final boolean CUSTOM_FORCE_INSERT = true;
+       private static final boolean DEFAULT_INSERT_REDIRECT = true;
+       private static final boolean CUSTOM_INSERT_REDIRECT = false;
+       private static final String DEFAULT_LAST_INSERT_HASH = null;
+       private static final String CUSTOM_LAST_INSERT_HASH = "last-insert-hash";
+       private static final int DEFAULT_LAST_INSERT_EDITION = 0;
+       private static final int CUSTOM_LAST_INSERT_EDITION = 12345;
+       private static final String DEFAULT_LAST_INSERT_FILENAME = null;
+       private static final String CUSTOM_LAST_INSERT_FILENAME = "filename.dat";
+       private static final String DEFAULT_CURRENT_HASH = null;
+       private static final String CUSTOM_CURRENT_HASH = "current-hash";
+       private static final Optional<?> DEFAULT_CHANGED_NAME = Optional.empty();
+       private static final String CUSTOM_CHANGED_NAME = "changed-name";
+       private static final String NULL_CHANGED_NAME = null;
+       private static final String ZERO_LENGTH_CHANGED_NAME = "";
+       private static final String CUSTOM_MIME_TYPE = "custom/mime-type";
+       private static final String NULL_MIME_TYPE = null;
+       private final FileOption fileOption = new FileOption(DEFAULT_MIME_TYPE);
+
+       @Test
+       public void defaultCustomKeyIsCHK() {
+               assertThat(fileOption.getCustomKey(), is(DEFAULT_CUSTOM_KEY));
+       }
+
+       @Test
+       public void customKeyIsRetainedCorrectly() {
+               fileOption.setCustomKey(CUSTOM_KEY);
+               assertThat(fileOption.getCustomKey(), is(CUSTOM_KEY));
+       }
+
+       @Test
+       public void nullCustomKeyIsTurnedIntoEmptyCustomKey() {
+               fileOption.setCustomKey(null);
+               assertThat(fileOption.getCustomKey(), is(EMPTY_CUSTOM_KEY));
+       }
+
+       @Test
+       public void defaultInsertIsTrue() {
+               assertThat(fileOption.isInsert(), is(DEFAULT_INSERT));
+       }
+
+       @Test
+       public void insertIsRetainedCorrectly() {
+               fileOption.setInsert(CUSTOM_INSERT);
+               assertThat(fileOption.isInsert(), is(CUSTOM_INSERT));
+       }
+
+       @Test
+       public void defaultForceInsertIsFalse() {
+               assertThat(fileOption.isForceInsert(), is(DEFAULT_FORCE_INSERT));
+       }
+
+       @Test
+       public void customForceInsertIsRetainedCorrectly() {
+               fileOption.setForceInsert(CUSTOM_FORCE_INSERT);
+               assertThat(fileOption.isForceInsert(), is(CUSTOM_FORCE_INSERT));
+       }
+
+       @Test
+       public void defaultInsertRedirectIsTrue() {
+               assertThat(fileOption.isInsertRedirect(), is(DEFAULT_INSERT_REDIRECT));
+       }
+
+       @Test
+       public void customInsertRedirectIsRetainedCorrectly() {
+               fileOption.setInsertRedirect(CUSTOM_INSERT_REDIRECT);
+               assertThat(fileOption.isInsertRedirect(), is(CUSTOM_INSERT_REDIRECT));
+       }
+
+       @Test
+       public void defaultLastInsertHashIsNull() {
+               assertThat(fileOption.getLastInsertHash(), is(DEFAULT_LAST_INSERT_HASH));
+       }
+
+       @Test
+       public void lastInsertHashIsRetainedCorrectly() {
+               fileOption.setLastInsertHash(CUSTOM_LAST_INSERT_HASH);
+               assertThat(fileOption.getLastInsertHash(), is(CUSTOM_LAST_INSERT_HASH));
+       }
+
+       @Test
+       public void defaultLastInsertEditionIsZero() {
+               assertThat(fileOption.getLastInsertEdition(), is(DEFAULT_LAST_INSERT_EDITION));
+       }
+
+       @Test
+       public void lastInsertEditionIsRetainedCorrectly() {
+               fileOption.setLastInsertEdition(CUSTOM_LAST_INSERT_EDITION);
+               assertThat(fileOption.getLastInsertEdition(), is(CUSTOM_LAST_INSERT_EDITION));
+       }
+
+       @Test
+       public void defaultLastInsertFilenameIsNull() {
+               assertThat(fileOption.getLastInsertFilename(), is(DEFAULT_LAST_INSERT_FILENAME));
+       }
+
+       @Test
+       public void lastInsertFilenameIsRetainedCorrectly() {
+               fileOption.setLastInsertFilename(CUSTOM_LAST_INSERT_FILENAME);
+               assertThat(fileOption.getLastInsertFilename(), is(CUSTOM_LAST_INSERT_FILENAME));
+       }
+
+       @Test
+       public void defaultCurrentHashIsNull() {
+               assertThat(fileOption.getCurrentHash(), is(DEFAULT_CURRENT_HASH));
+       }
+
+       @Test
+       public void currentHashIsRetainedCorrectly() {
+               fileOption.setCurrentHash(CUSTOM_CURRENT_HASH);
+               assertThat(fileOption.getCurrentHash(), is(CUSTOM_CURRENT_HASH));
+       }
+
+       @Test
+       public void defaultChangedNameIsEmpty() {
+               assertThat(fileOption.getChangedName(), is(DEFAULT_CHANGED_NAME));
+       }
+
+       @Test
+       public void changedNameIsRetainedCorrectly() {
+               fileOption.setChangedName(CUSTOM_CHANGED_NAME);
+               assertThat(fileOption.getChangedName().get(), is(CUSTOM_CHANGED_NAME));
+       }
+
+       @Test
+       public void nullSetsChangedNameToEmpty() {
+               fileOption.setChangedName(NULL_CHANGED_NAME);
+               assertThat(fileOption.getChangedName(), is(DEFAULT_CHANGED_NAME));
+       }
+
+       @Test
+       public void zeroLengthStringSetsChangedNameToEmpty() {
+               fileOption.setChangedName(ZERO_LENGTH_CHANGED_NAME);
+               assertThat(fileOption.getChangedName(), is(DEFAULT_CHANGED_NAME));
+       }
+
+       @Test
+       public void defaultMimeTypeIsTheOneGivenInTheConstructor() {
+               assertThat(fileOption.getMimeType(), is(DEFAULT_MIME_TYPE));
+       }
+
+       @Test
+       public void mimeTypeIsRetainedCorrectly() {
+               fileOption.setMimeType(CUSTOM_MIME_TYPE);
+               assertThat(fileOption.getMimeType(), is(CUSTOM_MIME_TYPE));
+       }
+
+       @Test
+       public void nullSetsMimeTypeBackToTheOneGivenInConstructor() {
+               fileOption.setMimeType(NULL_MIME_TYPE);
+               assertThat(fileOption.getMimeType(), is(DEFAULT_MIME_TYPE));
+       }
+
+       @Test
+       public void fileWithCustomInsertIsCustom() {
+               fileOption.setInsert(CUSTOM_INSERT);
+               assertThat(fileOption.isCustom(), is(true));
+       }
+
+       @Test
+       public void fileWithCustomKeyIsCustom() {
+               fileOption.setCustomKey(CUSTOM_KEY);
+               assertThat(fileOption.isCustom(), is(true));
+       }
+
+       @Test
+       public void fileWithChangedNameIsCustom() {
+               fileOption.setChangedName(CUSTOM_CHANGED_NAME);
+               assertThat(fileOption.isCustom(), is(true));
+       }
+
+       @Test
+       public void fileWithCustomMimeTypeIsCustom() {
+               fileOption.setMimeType(CUSTOM_MIME_TYPE);
+               assertThat(fileOption.isCustom(), is(true));
+       }
+
+       @Test
+       public void fileWithCustomInsertRedirectIsCustom() {
+               fileOption.setInsertRedirect(CUSTOM_INSERT_REDIRECT);
+               assertThat(fileOption.isCustom(), is(true));
+       }
+
+       @Test
+       public void unchangedFileIsNotCustom() {
+               assertThat(fileOption.isCustom(), is(false));
+       }
+
+       @Test
+       public void copyConstructorCopiesAllProperties() {
+               fileOption.setChangedName(CUSTOM_CHANGED_NAME);
+               fileOption.setInsertRedirect(CUSTOM_INSERT_REDIRECT);
+               fileOption.setInsert(CUSTOM_INSERT);
+               fileOption.setMimeType(CUSTOM_MIME_TYPE);
+               fileOption.setCurrentHash(CUSTOM_CURRENT_HASH);
+               fileOption.setCustomKey(CUSTOM_KEY);
+               fileOption.setForceInsert(CUSTOM_FORCE_INSERT);
+               fileOption.setLastInsertEdition(CUSTOM_LAST_INSERT_EDITION);
+               fileOption.setLastInsertFilename(CUSTOM_LAST_INSERT_FILENAME);
+               fileOption.setLastInsertHash(CUSTOM_LAST_INSERT_HASH);
+               fileOption.setCurrentHash(CUSTOM_CURRENT_HASH);
+               FileOption copiedFileOption = new FileOption(fileOption);
+               assertThat(copiedFileOption.getChangedName().get(), is(CUSTOM_CHANGED_NAME));
+               assertThat(copiedFileOption.isInsertRedirect(), is(CUSTOM_INSERT_REDIRECT));
+               assertThat(copiedFileOption.isInsert(), is(CUSTOM_INSERT));
+               assertThat(copiedFileOption.getMimeType(), is(CUSTOM_MIME_TYPE));
+               assertThat(copiedFileOption.getCurrentHash(), is(CUSTOM_CURRENT_HASH));
+               assertThat(copiedFileOption.getCustomKey(), is(CUSTOM_KEY));
+               assertThat(copiedFileOption.isForceInsert(), is(CUSTOM_FORCE_INSERT));
+               assertThat(copiedFileOption.getLastInsertEdition(), is(CUSTOM_LAST_INSERT_EDITION));
+               assertThat(copiedFileOption.getLastInsertFilename(), is(CUSTOM_LAST_INSERT_FILENAME));
+               assertThat(copiedFileOption.getLastInsertHash(), is(CUSTOM_LAST_INSERT_HASH));
+               assertThat(copiedFileOption.getCurrentHash(), is(CUSTOM_CURRENT_HASH));
+       }
+
+}
diff --git a/src/test/java/de/todesbaum/jsite/application/Freenet7InterfaceTest.java b/src/test/java/de/todesbaum/jsite/application/Freenet7InterfaceTest.java
new file mode 100644 (file)
index 0000000..1f19977
--- /dev/null
@@ -0,0 +1,193 @@
+package de.todesbaum.jsite.application;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import de.todesbaum.jsite.application.Freenet7Interface.ClientSupplier;
+import de.todesbaum.jsite.application.Freenet7Interface.ConnectionSupplier;
+import de.todesbaum.jsite.application.Freenet7Interface.NodeSupplier;
+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 org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+/**
+ * Unit test for {@link Freenet7Interface}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Freenet7InterfaceTest {
+
+       private static final String NODE_ADDRESS = "node-address";
+       private static final int NODE_PORT = 12345;
+       private static final String IDENTIFIER = "connection-identifier";
+       private static final String INSERT_URI = "insert-uri";
+       private static final String REQUEST_URI = "request-uri";
+
+       @Rule
+       public final ExpectedException expectedException = ExpectedException.none();
+
+       private final NodeSupplier nodeSupplier = mock(NodeSupplier.class);
+       private final ConnectionSupplier connectionSupplier = mock(ConnectionSupplier.class);
+       private final ClientSupplier clientSupplier = mock(ClientSupplier.class);
+       private final Freenet7Interface freenet7Interface = new Freenet7Interface(nodeSupplier, connectionSupplier, clientSupplier);
+
+       @Test
+       public void defaultConstructorCanBeCalled() {
+               new Freenet7Interface();
+       }
+
+       @Test
+       public void defaultConstructorCreatesDefaultNode() {
+           Freenet7Interface freenet7Interface = new Freenet7Interface();
+               freenet7Interface.setNode(new Node("foo", 12345));
+               de.todesbaum.util.freenet.fcp2.Node node = freenet7Interface.getNode();
+               assertThat(node.getHostname(), is("foo"));
+               assertThat(node.getPort(), is(12345));
+       }
+
+       @Test
+       public void withoutSettingANodeThereIsNoNode() {
+           assertThat(freenet7Interface.hasNode(), is(false));
+       }
+
+       @Test
+       public void afterSettingANodeThereIsANode() {
+               when(nodeSupplier.supply(anyString(), anyInt())).thenReturn(mock(de.todesbaum.util.freenet.fcp2.Node.class));
+               when(connectionSupplier.supply(any(de.todesbaum.util.freenet.fcp2.Node.class), anyString())).thenReturn(mock(Connection.class));
+           freenet7Interface.setNode(new Node("foo", 12345));
+           assertThat(freenet7Interface.hasNode(), is(true));
+       }
+
+       @Test
+       public void withoutConnectionThereIsNoNode() {
+               when(nodeSupplier.supply(anyString(), anyInt())).thenReturn(mock(de.todesbaum.util.freenet.fcp2.Node.class));
+           freenet7Interface.setNode(new Node("foo", 12345));
+           assertThat(freenet7Interface.hasNode(), is(false));
+       }
+
+       @Test
+       public void defaultConstructorCreatesDefaultConnection() {
+               Freenet7Interface freenet7Interface = new Freenet7Interface();
+               Connection connection = freenet7Interface.getConnection("foo");
+               assertThat(connection.getName(), is("foo"));
+       }
+
+       @Test
+       public void settingNodeAddressUsesNodeAndConnectionSuppliers() {
+               Node node = new Node(NODE_ADDRESS, NODE_PORT);
+               freenet7Interface.setNode(node);
+               verify(nodeSupplier).supply(NODE_ADDRESS, NODE_PORT);
+               verify(connectionSupplier).supply(eq(node), anyString());
+       }
+
+       @Test
+       public void settingNodeRetainsNodeCorrectly() {
+               Node node = new Node(NODE_ADDRESS, NODE_PORT);
+               Node realNode = mock(Node.class);
+               when(nodeSupplier.supply(NODE_ADDRESS, NODE_PORT)).thenReturn(realNode);
+               freenet7Interface.setNode(node);
+               assertThat(freenet7Interface.getNode(), is(realNode));
+       }
+
+       @Test
+       public void newConnectionCanBeCreated() {
+               Connection connection = mock(Connection.class);
+               when(connectionSupplier.supply(any(Node.class), eq(IDENTIFIER))).thenReturn(connection);
+               Connection returnedConnection = freenet7Interface.getConnection(IDENTIFIER);
+               assertThat(returnedConnection, is(connection));
+       }
+
+       @Test
+       public void interfaceHasNoNodeBeforeNodeIsSet() {
+               assertThat(freenet7Interface.hasNode(), is(false));
+       }
+
+       @Test
+       public void interfaceHasNodeOnceANodeWasSet() {
+               Connection connection = mock(Connection.class);
+               when(nodeSupplier.supply(anyString(), anyInt())).thenReturn(mock(Node.class));
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               assertThat(freenet7Interface.hasNode(), is(true));
+       }
+
+       @Test
+       public void interfaceHasNoNodeIfNodeIsSetToNull() {
+               Connection connection = mock(Connection.class);
+               when(nodeSupplier.supply(anyString(), anyInt())).thenReturn(mock(Node.class));
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               freenet7Interface.setNode(null);
+               assertThat(freenet7Interface.hasNode(), is(false));
+       }
+
+
+       @Test
+       public void nodeIsPresentIfConnectionIsConnected() throws IOException {
+               Connection connection = mock(Connection.class);
+               when(connection.isConnected()).thenReturn(true);
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               assertThat(freenet7Interface.isNodePresent(), is(true));
+       }
+
+       @Test
+       public void nodeIsPresentIfConnectionCanBeCreated() throws IOException {
+               Connection connection = mock(Connection.class);
+               when(connection.connect()).thenReturn(true);
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               assertThat(freenet7Interface.isNodePresent(), is(true));
+       }
+
+       @Test
+       public void exceptionIsThrownIfNodeIsNotPresentAndConnectionCanNotBeCreated() throws IOException {
+               Connection connection = mock(Connection.class);
+               doThrow(IOException.class).when(connection).connect();
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               expectedException.expect(IOException.class);
+               freenet7Interface.isNodePresent();
+       }
+
+       @Test
+       public void keyPairIsNotGeneratedIfNodeIsNotPresent() throws IOException {
+               Connection connection = mock(Connection.class);
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               expectedException.expect(IOException.class);
+               freenet7Interface.generateKeyPair();
+       }
+
+       @Test
+       public void keyPairIsGeneratedSuccessfully() throws IOException {
+               Connection connection = mock(Connection.class);
+               when(connection.isConnected()).thenReturn(true);
+               when(connectionSupplier.supply(any(Node.class), anyString())).thenReturn(connection);
+               freenet7Interface.setNode(mock(Node.class));
+               Message message = new Message("SSKKeyPair");
+               message.put("InsertURI", INSERT_URI);
+               message.put("RequestURI", REQUEST_URI);
+               Client client = mock(Client.class);
+               when(client.readMessage()).thenReturn(message);
+               when(clientSupplier.supply(eq(connection), any(GenerateSSK.class))).thenReturn(client);
+               String[] keyPair = freenet7Interface.generateKeyPair();
+               assertThat(keyPair[0], is(INSERT_URI));
+               assertThat(keyPair[1], is(REQUEST_URI));
+       }
+
+}
diff --git a/src/test/java/de/todesbaum/jsite/application/ProjectTest.java b/src/test/java/de/todesbaum/jsite/application/ProjectTest.java
new file mode 100644 (file)
index 0000000..df89de9
--- /dev/null
@@ -0,0 +1,22 @@
+package de.todesbaum.jsite.application;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link ProjectTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ProjectTest {
+
+       @Test
+       public void mimeTypeForTarBz2IsRecognizedCorrectly() {
+               Project project = new Project();
+               FileOption fileOption = project.getFileOption("foo.tar.bz2");
+               assertThat(fileOption.getMimeType(), is("application/x-gtar"));
+       }
+
+}