Merge branch 'italian-translation' into next
authorDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 31 May 2016 17:43:09 +0000 (19:43 +0200)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Tue, 31 May 2016 17:43:09 +0000 (19:43 +0200)
18 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/ProjectFilesPage.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/swing/TLabel.java
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..0becb74 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                <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 66efc09..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;
@@ -311,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 d9146a1..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;
 
@@ -365,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.
         *
@@ -462,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 1504a9f..c7dad3b 100644 (file)
@@ -25,21 +25,23 @@ 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;
@@ -65,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();
 
@@ -170,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();
        }
 
        /**
@@ -195,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()) {
@@ -204,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());
        }
 
        /**
@@ -280,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();
@@ -328,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;
@@ -349,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();
@@ -364,10 +358,10 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                putDir.setEarlyEncode(useEarlyEncode);
                putDir.setPriorityClass(priority);
                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;
@@ -433,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..ce6d96f 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;
@@ -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 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);
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 ea072d1..41eafdb 100644 (file)
@@ -44,6 +44,7 @@ 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.PriorityClass;
+import org.w3c.dom.Document;
 
 /**
  * The configuration.
@@ -128,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. */
@@ -429,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());
                                }
                        }
index 545a887..a6ac559 100644 (file)
@@ -197,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();
 
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/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"));
+       }
+
+}