From: David ‘Bombe’ Roden Date: Sun, 1 Apr 2012 16:30:06 +0000 (+0200) Subject: Merge branch 'release-0.10' X-Git-Tag: 0.10 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=refs%2Ftags%2F0.10;hp=7cec7da468700fd0f1eb5677edf437902eeae04b;p=jSite.git Merge branch 'release-0.10' --- diff --git a/.gitignore b/.gitignore index ba077a4..7dbcc35 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ bin +build/ +dist/ diff --git a/build.xml b/build.xml index 9725f07..d997bd1 100644 --- a/build.xml +++ b/build.xml @@ -26,14 +26,14 @@ - + - + diff --git a/src/de/todesbaum/jsite/application/AbortedException.java b/src/de/todesbaum/jsite/application/AbortedException.java index bde6953..0dfd8c7 100644 --- a/src/de/todesbaum/jsite/application/AbortedException.java +++ b/src/de/todesbaum/jsite/application/AbortedException.java @@ -1,5 +1,5 @@ /* - * jSite - AbortedException.java - Copyright © 2010 David Roden + * jSite - AbortedException.java - Copyright © 2010–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/application/FileOption.java b/src/de/todesbaum/jsite/application/FileOption.java index 31e6698..c8824de 100644 --- a/src/de/todesbaum/jsite/application/FileOption.java +++ b/src/de/todesbaum/jsite/application/FileOption.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet Copyright (C) 2006 David - * Roden + * jSite - FileOption.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software @@ -38,21 +37,27 @@ public class FileOption { /** The default changed name. */ private static final String DEFAULT_CHANGED_NAME = null; - /** The default container. */ - private static final String DEFAULT_CONTAINER = ""; - - /** The default edition range. */ - private static final int DEFAULT_EDITION_RANGE = 3; - - /** The default for the replace edition state. */ - private static final boolean DEFAULT_REPLACE_EDITION = false; - /** The insert state. */ private boolean insert; + /** Whether to force an insert. */ + private boolean forceInsert; + /** Whether to insert a redirect. */ private boolean insertRedirect; + /** The hash of the last insert. */ + private String lastInsertHash; + + /** The edition of the last insert. */ + private int lastInsertEdition; + + /** The filename of the last insert. */ + private String lastInsertFilename; + + /** The current hash of the file. */ + private String currentHash; + /** The custom key. */ private String customKey; @@ -65,15 +70,6 @@ public class FileOption { /** The current MIME type. */ private String mimeType; - /** The container. */ - private String container; - - /** The edition range. */ - private int editionRange; - - /** The replace edition state. */ - private boolean replaceEdition; - /** * Creates new file options. * @@ -87,9 +83,6 @@ public class FileOption { changedName = DEFAULT_CHANGED_NAME; this.defaultMimeType = defaultMimeType; mimeType = defaultMimeType; - container = DEFAULT_CONTAINER; - editionRange = DEFAULT_EDITION_RANGE; - replaceEdition = DEFAULT_REPLACE_EDITION; } /** @@ -145,6 +138,31 @@ public class FileOption { } /** + * Returns whether the insert of this file should be forced, even if its + * current hash matches the last insert hash. + * + * @return {@code true} to force the insert of this file, {@code false} + * otherwise + */ + public boolean isForceInsert() { + return forceInsert; + } + + /** + * Sets whether to force the insert of this file, even if its current hash + * matches the last insert hash. + * + * @param forceInsert + * {@code true} to force the insert of this file, {@code false} + * otherwise + * @return These file options + */ + public FileOption setForceInsert(boolean forceInsert) { + this.forceInsert = forceInsert; + return this; + } + + /** * Returns whether a redirect to a different key should be inserted. This * will only matter if {@link #isInsert()} returns {@code false}. The key * that should be redirected to still needs to be specified via @@ -173,6 +191,93 @@ public class FileOption { } /** + * Returns the hash of the file when it was last inserted + * + * @return The last hash of the file + */ + public String getLastInsertHash() { + return lastInsertHash; + } + + /** + * Sets the hash of the file when it was last inserted. + * + * @param lastInsertHash + * The last hash of the file + * @return These file options + */ + public FileOption setLastInsertHash(String lastInsertHash) { + this.lastInsertHash = lastInsertHash; + return this; + } + + /** + * Returns the last edition at which this file was inserted. + * + * @return The last insert edition of this file + */ + public int getLastInsertEdition() { + return lastInsertEdition; + } + + /** + * Sets the last insert edition of this file. + * + * @param lastInsertEdition + * The last insert edition of this file + * @return These file options + */ + public FileOption setLastInsertEdition(int lastInsertEdition) { + this.lastInsertEdition = lastInsertEdition; + return this; + } + + /** + * Returns the name of the file when it was last inserted. + * + * @return The name of the file at the last insert + */ + public String getLastInsertFilename() { + return lastInsertFilename; + } + + /** + * Sets the name of the file when it was last inserted. + * + * @param lastInsertFilename + * The name of the file at the last insert. + * @return These file options + */ + public FileOption setLastInsertFilename(String lastInsertFilename) { + this.lastInsertFilename = lastInsertFilename; + return this; + } + + /** + * Returns the current hash of the file. This value is ony a temporary value + * that is copied to {@link #getLastInsertHash()} when a project has + * finished inserting. + * + * @see Project#onSuccessfulInsert() + * @return The current hash of the file + */ + public String getCurrentHash() { + return currentHash; + } + + /** + * Sets the current hash of the file. + * + * @param currentHash + * The current hash of the file + * @return These file options + */ + public FileOption setCurrentHash(String currentHash) { + this.currentHash = currentHash; + return this; + } + + /** * Returns whether this file has a changed name. Use * {@link #getChangedName()} is this method returns {@code true}. * @@ -231,69 +336,6 @@ public class FileOption { } /** - * Returns the name of the container this file should be put in. - * - * @return The name of the container - */ - public String getContainer() { - return container; - } - - /** - * Sets the name of the container this file should be put in. - * - * @param container - * The name of the container - */ - public void setContainer(String container) { - if (container == null) { - this.container = DEFAULT_CONTAINER; - } else { - this.container = container; - } - } - - /** - * Sets whether the file should have “$[EDITION+n]” tags replaced. - * - * @param replaceEdition - * true to replace tags, false not to - * replace - */ - public void setReplaceEdition(boolean replaceEdition) { - this.replaceEdition = replaceEdition; - } - - /** - * Returns whether the file should have “$[EDITION+n]” tags replaced. - * - * @return true if tags should be replaced, false - * otherwise - */ - public boolean getReplaceEdition() { - return replaceEdition; - } - - /** - * Sets the range of editions that should be replaced. - * - * @param editionRange - * The range editions to replace - */ - public void setEditionRange(int editionRange) { - this.editionRange = editionRange; - } - - /** - * Returns the range of editions that should be replaced. - * - * @return The range of editions to replace - */ - public int getEditionRange() { - return editionRange; - } - - /** * Returns whether the options for this file have been modified, i.e. are * not at their default values. * @@ -313,15 +355,6 @@ public class FileOption { if (!defaultMimeType.equals(mimeType)) { return true; } - if (!DEFAULT_CONTAINER.equals(container)) { - return true; - } - if (replaceEdition != DEFAULT_REPLACE_EDITION) { - return true; - } - if (editionRange != DEFAULT_EDITION_RANGE) { - return true; - } if (insertRedirect != DEFAULT_INSERT_REDIRECT) { return true; } diff --git a/src/de/todesbaum/jsite/application/Freenet7Interface.java b/src/de/todesbaum/jsite/application/Freenet7Interface.java index a271f6b..78250b2 100644 --- a/src/de/todesbaum/jsite/application/Freenet7Interface.java +++ b/src/de/todesbaum/jsite/application/Freenet7Interface.java @@ -1,6 +1,5 @@ /* - * jSite - - * Copyright (C) 2006 David Roden + * jSite - Freenet7Interface.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -142,7 +141,7 @@ public class Freenet7Interface { */ public String[] generateKeyPair() throws IOException { if (!isNodePresent()) { - return null; + throw new IOException("Node is offline."); } GenerateSSK generateSSK = new GenerateSSK(); Client client = new Client(connection, generateSSK); diff --git a/src/de/todesbaum/jsite/application/InsertListener.java b/src/de/todesbaum/jsite/application/InsertListener.java index 40cdaf1..9b6b332 100644 --- a/src/de/todesbaum/jsite/application/InsertListener.java +++ b/src/de/todesbaum/jsite/application/InsertListener.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - InsertListener.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/application/KeyDialog.java b/src/de/todesbaum/jsite/application/KeyDialog.java index 536f8c6..4b64f72 100644 --- a/src/de/todesbaum/jsite/application/KeyDialog.java +++ b/src/de/todesbaum/jsite/application/KeyDialog.java @@ -1,5 +1,5 @@ /* - * jSite - KeyDialog.java - Copyright © 2010 David Roden + * jSite - KeyDialog.java - Copyright © 2010–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/application/Node.java b/src/de/todesbaum/jsite/application/Node.java index aae1f7a..df7492f 100644 --- a/src/de/todesbaum/jsite/application/Node.java +++ b/src/de/todesbaum/jsite/application/Node.java @@ -1,6 +1,5 @@ /* - * jSite-0.7 - - * Copyright (C) 2006 David Roden + * jSite - Node.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/application/Project.java b/src/de/todesbaum/jsite/application/Project.java index 669b782..fa0b774 100644 --- a/src/de/todesbaum/jsite/application/Project.java +++ b/src/de/todesbaum/jsite/application/Project.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet Copyright (C) 2006 David - * Roden + * jSite - Project.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software @@ -23,6 +22,7 @@ import java.io.File; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; import de.todesbaum.util.mime.DefaultMIMETypes; @@ -420,4 +420,22 @@ public class Project implements Comparable { return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/"; } + /** + * Performs some post-processing on the project after it was inserted + * successfully. At the moment it copies the current hashes of all file + * options to the last insert hashes, updating the hashes for the next + * insert. + */ + public void onSuccessfulInsert() { + for (Entry fileOptionEntry : fileOptions.entrySet()) { + FileOption fileOption = fileOptionEntry.getValue(); + if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) { + fileOption.setLastInsertEdition(edition); + fileOption.setLastInsertHash(fileOption.getCurrentHash()); + fileOption.setLastInsertFilename(fileOption.hasChangedName() ? fileOption.getChangedName() : fileOptionEntry.getKey()); + } + fileOption.setForceInsert(false); + } + } + } diff --git a/src/de/todesbaum/jsite/application/ProjectInserter.java b/src/de/todesbaum/jsite/application/ProjectInserter.java index 756e121..3a49c49 100644 --- a/src/de/todesbaum/jsite/application/ProjectInserter.java +++ b/src/de/todesbaum/jsite/application/ProjectInserter.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - ProjectInserter.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,40 +18,35 @@ package de.todesbaum.jsite.application; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; import de.todesbaum.jsite.gui.FileScanner; +import de.todesbaum.jsite.gui.FileScanner.ScannedFile; import de.todesbaum.jsite.gui.FileScannerListener; import de.todesbaum.util.freenet.fcp2.Client; import de.todesbaum.util.freenet.fcp2.ClientPutComplexDir; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; import de.todesbaum.util.freenet.fcp2.Connection; import de.todesbaum.util.freenet.fcp2.DirectFileEntry; import de.todesbaum.util.freenet.fcp2.FileEntry; import de.todesbaum.util.freenet.fcp2.Message; +import de.todesbaum.util.freenet.fcp2.PriorityClass; import de.todesbaum.util.freenet.fcp2.RedirectFileEntry; import de.todesbaum.util.freenet.fcp2.Verbosity; -import de.todesbaum.util.io.Closer; -import de.todesbaum.util.io.ReplacingOutputStream; -import de.todesbaum.util.io.StreamCopier; +import de.todesbaum.util.io.StreamCopier.ProgressListener; /** * Manages project inserts. @@ -94,6 +88,18 @@ public class ProjectInserter implements FileScannerListener, Runnable { /** Whether the insert is cancelled. */ private volatile boolean cancelled = false; + /** Progress listener for payload transfers. */ + private ProgressListener progressListener; + + /** Whether to use “early encode.” */ + private boolean useEarlyEncode; + + /** The insert priority. */ + private PriorityClass priority; + + /** The manifest putter. */ + private ManifestPutter manifestPutter; + /** * Adds a listener to the list of registered listeners. * @@ -220,10 +226,45 @@ public class ProjectInserter implements FileScannerListener, Runnable { } /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + this.useEarlyEncode = useEarlyEncode; + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + this.priority = priority; + } + + /** + * Sets the manifest putter to use for inserts. + * + * @param manifestPutter + * The manifest putter to use + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + this.manifestPutter = manifestPutter; + } + + /** * Starts the insert. + * + * @param progressListener + * Listener to notify on progress events */ - public void start() { + public void start(ProgressListener progressListener) { cancelled = false; + this.progressListener = progressListener; fileScanner = new FileScanner(project); fileScanner.addFileScannerListener(this); new Thread(fileScanner).start(); @@ -262,148 +303,47 @@ public class ProjectInserter implements FileScannerListener, Runnable { private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException { File file = new File(project.getLocalPath(), filename); length[0] = file.length(); - if (!fileOption.getReplaceEdition()) { - return new FileInputStream(file); - } - ByteArrayOutputStream filteredByteOutputStream = new ByteArrayOutputStream(Math.min(Integer.MAX_VALUE, (int) length[0])); - ReplacingOutputStream outputStream = new ReplacingOutputStream(filteredByteOutputStream); - FileInputStream fileInput = new FileInputStream(file); - try { - outputStream.addReplacement("$[EDITION]", String.valueOf(edition)); - outputStream.addReplacement("$[URI]", project.getFinalRequestURI(0)); - for (int index = 1; index <= fileOption.getEditionRange(); index++) { - outputStream.addReplacement("$[URI+" + index + "]", project.getFinalRequestURI(index)); - outputStream.addReplacement("$[EDITION+" + index + "]", String.valueOf(edition + index)); - } - StreamCopier.copy(fileInput, outputStream, length[0]); - } finally { - Closer.close(fileInput); - Closer.close(outputStream); - Closer.close(filteredByteOutputStream); - } - byte[] filteredBytes = filteredByteOutputStream.toByteArray(); - length[0] = filteredBytes.length; - return new ByteArrayInputStream(filteredBytes); - } - - /** - * Creates an input stream for a container. - * - * @param containerFiles - * All container definitions - * @param containerName - * The name of the container to create - * @param edition - * The current edition - * @param containerLength - * An array containing a single long which is used to - * return the final length of the container stream, - * after all replacements - * @return The input stream for the container - * @throws IOException - * if an I/O error occurs - */ - private InputStream createContainerInputStream(Map> containerFiles, String containerName, int edition, long[] containerLength) throws IOException { - File tempFile = File.createTempFile("jsite", ".zip", (tempDirectory == null) ? null : new File(tempDirectory)); - tempFile.deleteOnExit(); - FileOutputStream fileOutputStream = new FileOutputStream(tempFile); - ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); - try { - for (String filename : containerFiles.get(containerName)) { - File dataFile = new File(project.getLocalPath(), filename); - if (dataFile.exists()) { - ZipEntry zipEntry = new ZipEntry(filename); - long[] fileLength = new long[1]; - InputStream wrappedInputStream = createFileInputStream(filename, project.getFileOption(filename), edition, fileLength); - try { - zipOutputStream.putNextEntry(zipEntry); - StreamCopier.copy(wrappedInputStream, zipOutputStream, fileLength[0]); - } finally { - zipOutputStream.closeEntry(); - wrappedInputStream.close(); - } - } - } - } finally { - zipOutputStream.closeEntry(); - Closer.close(zipOutputStream); - Closer.close(fileOutputStream); - } - - containerLength[0] = tempFile.length(); - return new FileInputStream(tempFile); + return new FileInputStream(file); } /** * Creates a file entry suitable for handing in to * {@link ClientPutComplexDir#addFileEntry(FileEntry)}. * - * @param filename - * The name of the file to insert + * @param file + * The name and hash of the file to insert * @param edition * The current edition - * @param containerFiles - * The container definitions * @return A file entry for the given file */ - private FileEntry createFileEntry(String filename, int edition, Map> containerFiles) { + private FileEntry createFileEntry(ScannedFile file, int edition) { FileEntry fileEntry = null; + String filename = file.getFilename(); FileOption fileOption = project.getFileOption(filename); - if (filename.startsWith("/container/:")) { - String containerName = filename.substring("/container/:".length()); + if (fileOption.isInsert()) { + fileOption.setCurrentHash(file.getHash()); + /* check if file was modified. */ + if (!fileOption.isForceInsert() && file.getHash().equals(fileOption.getLastInsertHash())) { + /* only insert a redirect. */ + logger.log(Level.FINE, String.format("Inserting redirect to edition %d for %s.", fileOption.getLastInsertEdition(), filename)); + return new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename()); + } try { - long[] containerLength = new long[1]; - InputStream containerInputStream = createContainerInputStream(containerFiles, containerName, edition, containerLength); - fileEntry = new DirectFileEntry(containerName + ".zip", "application/zip", containerInputStream, containerLength[0]); + long[] fileLength = new long[1]; + InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength); + fileEntry = new DirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileEntryInputStream, fileLength[0]); } catch (IOException ioe1) { /* ignore, null is returned. */ } } else { - if (fileOption.isInsert()) { - try { - long[] fileLength = new long[1]; - InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength); - fileEntry = new DirectFileEntry(filename, project.getFileOption(filename).getMimeType(), fileEntryInputStream, fileLength[0]); - } catch (IOException ioe1) { - /* ignore, null is returned. */ - } - } else { - if (fileOption.isInsertRedirect()) { - fileEntry = new RedirectFileEntry(filename, fileOption.getMimeType(), fileOption.getCustomKey()); - } + if (fileOption.isInsertRedirect()) { + fileEntry = new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileOption.getCustomKey()); } } return fileEntry; } /** - * Creates container definitions. - * - * @param files - * The list of all files - * @param containers - * The list of all containers - * @param containerFiles - * Empty map that will be filled with container definitions - */ - private void createContainers(List files, List containers, Map> containerFiles) { - for (String filename : new ArrayList(files)) { - FileOption fileOption = project.getFileOption(filename); - String containerName = fileOption.getContainer(); - if (!containerName.equals("")) { - if (!containers.contains(containerName)) { - containers.add(containerName); - containerFiles.put(containerName, new ArrayList()); - /* hmm. looks like a hack to me. */ - files.add("/container/:" + containerName); - } - containerFiles.get(containerName).add(filename); - files.remove(filename); - } - } - } - - /** * Validates the given project. The project will be checked for any invalid * conditions, such as invalid insert or request keys, missing path names, * missing default file, and so on. @@ -429,17 +369,14 @@ public class ProjectInserter implements FileScannerListener, Runnable { } } String indexFile = project.getIndexFile(); - boolean hasIndexFile = (indexFile != null); - if (hasIndexFile && !project.getFileOption(indexFile).getContainer().equals("")) { - checkReport.addIssue("warning.container-index", false); - } + boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0); List allowedIndexContentTypes = Arrays.asList("text/html", "application/xhtml+xml"); if (hasIndexFile && !allowedIndexContentTypes.contains(project.getFileOption(indexFile).getMimeType())) { checkReport.addIssue("warning.index-not-html", false); } Map fileOptions = project.getFileOptions(); Set> fileOptionEntries = fileOptions.entrySet(); - boolean insert = false; + boolean insert = fileOptionEntries.isEmpty(); for (Entry fileOptionEntry : fileOptionEntries) { String fileName = fileOptionEntry.getKey(); FileOption fileOption = fileOptionEntry.getValue(); @@ -478,7 +415,7 @@ public class ProjectInserter implements FileScannerListener, Runnable { */ public void run() { fireProjectInsertStarted(); - List files = fileScanner.getFiles(); + List files = fileScanner.getFiles(); /* create connection to node */ synchronized (lockObject) { @@ -500,11 +437,6 @@ public class ProjectInserter implements FileScannerListener, Runnable { Client client = new Client(connection); - /* create containers */ - final List containers = new ArrayList(); - final Map> containerFiles = new HashMap>(); - createContainers(files, containers, containerFiles); - /* collect files */ int edition = project.getEdition(); String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/"; @@ -514,9 +446,11 @@ public class ProjectInserter implements FileScannerListener, Runnable { } putDir.setVerbosity(Verbosity.ALL); putDir.setMaxRetries(-1); - putDir.setEarlyEncode(false); - for (String filename : files) { - FileEntry fileEntry = createFileEntry(filename, edition, containerFiles); + putDir.setEarlyEncode(useEarlyEncode); + putDir.setPriorityClass(priority); + putDir.setManifestPutter(manifestPutter); + for (ScannedFile file : files) { + FileEntry fileEntry = createFileEntry(file, edition); if (fileEntry != null) { try { putDir.addFileEntry(fileEntry); @@ -529,7 +463,8 @@ public class ProjectInserter implements FileScannerListener, Runnable { /* start request */ try { - client.execute(putDir); + client.execute(putDir, progressListener); + fireProjectUploadFinished(); } catch (IOException ioe1) { fireProjectInsertFinished(false, ioe1); return; @@ -537,17 +472,12 @@ public class ProjectInserter implements FileScannerListener, Runnable { /* parse progress and success messages */ String finalURI = null; - boolean firstMessage = true; boolean success = false; boolean finished = false; boolean disconnected = false; while (!finished && !cancelled) { Message message = client.readMessage(); finished = (message == null) || (disconnected = client.isDisconnected()); - if (firstMessage) { - fireProjectUploadFinished(); - firstMessage = false; - } logger.log(Level.FINE, "Received message: " + message); if (!finished) { @SuppressWarnings("null") @@ -564,8 +494,8 @@ public class ProjectInserter implements FileScannerListener, Runnable { boolean finalized = Boolean.parseBoolean(message.get("FinalizedTotal")); fireProjectInsertProgress(succeeded, failed, fatal, total, finalized); } - success = "PutSuccessful".equals(messageName); - finished = success || "PutFailed".equals(messageName) || messageName.endsWith("Error"); + success |= "PutSuccessful".equals(messageName); + finished = (success && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error"); } } @@ -576,6 +506,7 @@ public class ProjectInserter implements FileScannerListener, Runnable { int newEdition = Integer.parseInt(editionPart); project.setEdition(newEdition); project.setLastInsertionTime(System.currentTimeMillis()); + project.onSuccessfulInsert(); } fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null)); } diff --git a/src/de/todesbaum/jsite/application/UpdateChecker.java b/src/de/todesbaum/jsite/application/UpdateChecker.java index a80eb4f..c49f166 100644 --- a/src/de/todesbaum/jsite/application/UpdateChecker.java +++ b/src/de/todesbaum/jsite/application/UpdateChecker.java @@ -1,6 +1,5 @@ /* - * jSite-remote - UpdateChecker.java - - * Copyright © 2008 David Roden + * jSite - UpdateChecker.java - Copyright © 2008–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,7 +51,7 @@ public class UpdateChecker implements Runnable { private static int counter = 0; /** The edition for the update check URL. */ - private static final int UPDATE_EDITION = 11; + private static final int UPDATE_EDITION = 17; /** The URL for update checks. */ private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE"; @@ -216,6 +215,9 @@ public class UpdateChecker implements Runnable { while (!stop) { Message message = client.readMessage(); logger.log(Level.FINEST, "Received message: " + message); + if (message == null) { + break; + } if ("GetFailed".equals(message.getName())) { if ("27".equals(message.get("code"))) { String editionString = message.get("redirecturi").split("/")[2]; diff --git a/src/de/todesbaum/jsite/application/UpdateListener.java b/src/de/todesbaum/jsite/application/UpdateListener.java index fbf7b91..a37440c 100644 --- a/src/de/todesbaum/jsite/application/UpdateListener.java +++ b/src/de/todesbaum/jsite/application/UpdateListener.java @@ -1,6 +1,5 @@ /* - * jSite-remote - UpdateListener.java - - * Copyright © 2008 David Roden + * jSite - UpdateListener.java - Copyright © 2008–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/gui/FileScanner.java b/src/de/todesbaum/jsite/gui/FileScanner.java index 8ef1340..1790fbf 100644 --- a/src/de/todesbaum/jsite/gui/FileScanner.java +++ b/src/de/todesbaum/jsite/gui/FileScanner.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - FileScanner.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,13 +20,23 @@ package de.todesbaum.jsite.gui; import java.io.File; import java.io.FileFilter; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import de.todesbaum.jsite.application.Project; import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.util.io.Closer; +import de.todesbaum.util.io.StreamCopier; /** * Scans the local path of a project anychronously and returns the list of found @@ -39,6 +48,9 @@ import de.todesbaum.jsite.i18n.I18n; */ public class FileScanner implements Runnable { + /** The logger. */ + private final static Logger logger = Logger.getLogger(FileScanner.class.getName()); + /** The list of listeners. */ private final List fileScannerListeners = new ArrayList(); @@ -46,7 +58,7 @@ public class FileScanner implements Runnable { private final Project project; /** The list of found files. */ - private List files; + private List files; /** Wether there was an error. */ private boolean error = false; @@ -99,7 +111,7 @@ public class FileScanner implements Runnable { * @see FileScannerListener#fileScannerFinished(FileScanner) */ public void run() { - files = new ArrayList(); + files = new ArrayList(); error = false; try { scanFiles(new File(project.getLocalPath()), files); @@ -125,7 +137,7 @@ public class FileScanner implements Runnable { * * @return The list of found files */ - public List getFiles() { + public List getFiles() { return files; } @@ -139,7 +151,7 @@ public class FileScanner implements Runnable { * @throws IOException * if an I/O error occurs */ - private void scanFiles(File rootDir, List fileList) throws IOException { + private void scanFiles(File rootDir, List fileList) throws IOException { File[] files = rootDir.listFiles(new FileFilter() { @SuppressWarnings("synthetic-access") @@ -155,10 +167,178 @@ public class FileScanner implements Runnable { scanFiles(file, fileList); continue; } - String filename = project.shortenFilename(file); - filename = filename.replace('\\', '/'); - fileList.add(filename); + String filename = project.shortenFilename(file).replace('\\', '/'); + String hash = hashFile(project.getLocalPath(), filename); + fileList.add(new ScannedFile(filename, hash)); + } + } + + /** + * Hashes the given file. + * + * @param path + * The path of the project + * @param filename + * The name of the file, relative to the project path + * @return The hash of the file + */ + @SuppressWarnings("synthetic-access") + private static String hashFile(String path, String filename) { + InputStream fileInputStream = null; + DigestOutputStream digestOutputStream = null; + File file = new File(path, filename); + try { + fileInputStream = new FileInputStream(file); + digestOutputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256")); + StreamCopier.copy(fileInputStream, digestOutputStream, file.length()); + return toHex(digestOutputStream.getMessageDigest().digest()); + } catch (NoSuchAlgorithmException nsae1) { + logger.log(Level.WARNING, "Could not get SHA-256 digest!", nsae1); + } catch (IOException ioe1) { + logger.log(Level.WARNING, "Could not read file!", ioe1); + } finally { + Closer.close(digestOutputStream); + Closer.close(fileInputStream); + } + return toHex(new byte[32]); + } + + /** + * Converts the given byte array into a hexadecimal string. + * + * @param array + * The array to convert + * @return The hexadecimal string + */ + private static String toHex(byte[] array) { + StringBuilder hexString = new StringBuilder(array.length * 2); + for (byte b : array) { + hexString.append("0123456789abcdef".charAt((b >>> 4) & 0x0f)).append("0123456789abcdef".charAt(b & 0xf)); + } + return hexString.toString(); + } + + /** + * {@link OutputStream} that discards all written bytes. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + private static class NullOutputStream extends OutputStream { + + /** + * {@inheritDoc} + */ + @Override + public void write(int b) { + /* do nothing. */ + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] b) { + /* do nothing. */ + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] b, int off, int len) { + /* do nothing. */ } + + } + + /** + * Container for a scanned file, consisting of the name of the file and its + * hash. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + public static class ScannedFile implements Comparable { + + /** The name of the file. */ + private final String filename; + + /** The hash of the file. */ + private final String hash; + + /** + * Creates a new scanned file. + * + * @param filename + * The name of the file + * @param hash + * The hash of the file + */ + public ScannedFile(String filename, String hash) { + this.filename = filename; + this.hash = hash; + } + + // + // ACCESSORS + // + + /** + * Returns the name of the file. + * + * @return The name of the file + */ + public String getFilename() { + return filename; + } + + /** + * Returns the hash of the file. + * + * @return The hash of the file + */ + public String getHash() { + return hash; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return filename.hashCode(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) { + return filename.equals(obj); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return filename; + } + + // + // COMPARABLE METHODS + // + + /** + * {@inheritDoc} + */ + public int compareTo(ScannedFile scannedFile) { + return filename.compareTo(scannedFile.filename); + } + } -} \ No newline at end of file +} diff --git a/src/de/todesbaum/jsite/gui/FileScannerListener.java b/src/de/todesbaum/jsite/gui/FileScannerListener.java index 838a2ed..63e2c3d 100644 --- a/src/de/todesbaum/jsite/gui/FileScannerListener.java +++ b/src/de/todesbaum/jsite/gui/FileScannerListener.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - FileScannerListener.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/gui/NodeManagerListener.java b/src/de/todesbaum/jsite/gui/NodeManagerListener.java index d6a9404..760c967 100644 --- a/src/de/todesbaum/jsite/gui/NodeManagerListener.java +++ b/src/de/todesbaum/jsite/gui/NodeManagerListener.java @@ -1,6 +1,5 @@ /* - * jSite-0.7 - - * Copyright (C) 2006 David Roden + * jSite - NodeManagerListener.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,4 +38,12 @@ public interface NodeManagerListener extends EventListener { */ public void nodesUpdated(Node[] nodes); + /** + * Notifies a listener that the selected node has changed. + * + * @param node + * The new selected node + */ + public void nodeSelected(Node node); + } diff --git a/src/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/de/todesbaum/jsite/gui/NodeManagerPage.java index 951a912..8e44129 100644 --- a/src/de/todesbaum/jsite/gui/NodeManagerPage.java +++ b/src/de/todesbaum/jsite/gui/NodeManagerPage.java @@ -1,6 +1,5 @@ /* - * jSite-0.7 - - * Copyright (C) 2006 David Roden + * jSite - NodeManagerPage.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -144,6 +143,18 @@ public class NodeManagerPage extends TWizardPage implements ListSelectionListene } /** + * Notifies all listeners that a new node was selected. + * + * @param node + * The newly selected node + */ + protected void fireNodeSelected(Node node) { + for (NodeManagerListener nodeManagerListener : nodeManagerListeners) { + nodeManagerListener.nodeSelected(node); + } + } + + /** * Creates all actions. */ private void createActions() { @@ -331,6 +342,7 @@ public class NodeManagerPage extends TWizardPage implements ListSelectionListene private void addNode() { Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node")); nodeListModel.addElement(node); + deleteNodeAction.setEnabled(nodeListModel.size() > 1); wizard.setNextEnabled(true); fireNodesUpdated(getNodes()); } @@ -346,9 +358,12 @@ public class NodeManagerPage extends TWizardPage implements ListSelectionListene if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.node-manager.delete-node.warning"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.CANCEL_OPTION) { return; } + int nodeIndex = nodeListModel.indexOf(node); nodeListModel.removeElement(node); nodeList.repaint(); + fireNodeSelected((Node) nodeListModel.get(Math.min(nodeIndex, nodeListModel.size() - 1))); fireNodesUpdated(getNodes()); + deleteNodeAction.setEnabled(nodeListModel.size() > 1); wizard.setNextEnabled(nodeListModel.size() > 0); } @@ -370,7 +385,7 @@ public class NodeManagerPage extends TWizardPage implements ListSelectionListene nodeNameTextField.setEnabled(enabled); nodeHostnameTextField.setEnabled(enabled); nodePortSpinner.setEnabled(enabled); - deleteNodeAction.setEnabled(enabled); + deleteNodeAction.setEnabled(enabled && (nodeListModel.size() > 1)); if (enabled) { nodeNameTextField.setText(node.getName()); nodeHostnameTextField.setText(node.getHostname()); @@ -426,6 +441,7 @@ public class NodeManagerPage extends TWizardPage implements ListSelectionListene JSpinner sourceSpinner = (JSpinner) source; if ("node-port".equals(sourceSpinner.getName())) { selectedNode.setPort((Integer) sourceSpinner.getValue()); + fireNodeSelected(selectedNode); nodeList.repaint(); } } diff --git a/src/de/todesbaum/jsite/gui/PreferencesPage.java b/src/de/todesbaum/jsite/gui/PreferencesPage.java index c4b4ce3..5cf258a 100644 --- a/src/de/todesbaum/jsite/gui/PreferencesPage.java +++ b/src/de/todesbaum/jsite/gui/PreferencesPage.java @@ -1,6 +1,5 @@ /* - * jSite - PreferencesPage.java - - * Copyright © 2009 David Roden + * jSite - PreferencesPage.java - Copyright © 2009–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,8 +26,11 @@ import java.awt.event.ActionEvent; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; @@ -37,6 +39,9 @@ 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; @@ -56,18 +61,60 @@ public class PreferencesPage extends TWizardPage { /** Action that chooses a new temp directory. */ private Action chooseTempDirectoryAction; + /** Action when selecting “next to JAR file.” */ + private Action nextToJarFileAction; + + /** Action when selecting “home directory.” */ + private Action homeDirectoryAction; + + /** Action when selecting “custom directory.” */ + private Action customDirectoryAction; + + /** Action when selecting “use early encode.” */ + private Action useEarlyEncodeAction; + + /** Action when a priority was selected. */ + private Action priorityAction; + /** The text field containing the directory. */ private JTextField tempDirectoryTextField; /** The temp directory. */ private String tempDirectory; + /** The configuration location. */ + private ConfigurationLocation configurationLocation; + + /** Whether to use “early encode.” */ + private boolean useEarlyEncode; + + /** The prioriy for inserts. */ + private PriorityClass priority; + /** The “default” button. */ private JRadioButton defaultTempDirectory; /** The “custom” button. */ private JRadioButton customTempDirectory; + /** The “next to JAR file” checkbox. */ + private JRadioButton nextToJarFile; + + /** The “home directory” checkbox. */ + private JRadioButton homeDirectory; + + /** The “custom directory” checkbox. */ + private JRadioButton customDirectory; + + /** The “use early encode” checkbox. */ + private JCheckBox useEarlyEncodeCheckBox; + + /** The insert priority select box. */ + private JComboBox insertPriorityComboBox; + + /** The manifest putter select box. */ + private JComboBox manifestPutterComboBox; + /** * Creates a new “preferences” page. * @@ -124,6 +171,119 @@ public class PreferencesPage extends TWizardPage { } /** + * Returns the configuration location. + * + * @return The configuration location + */ + public ConfigurationLocation getConfigurationLocation() { + return configurationLocation; + } + + /** + * Sets the configuration location. + * + * @param configurationLocation + * The configuration location + */ + public void setConfigurationLocation(ConfigurationLocation configurationLocation) { + this.configurationLocation = configurationLocation; + switch (configurationLocation) { + case NEXT_TO_JAR_FILE: + nextToJarFile.setSelected(true); + break; + case HOME_DIRECTORY: + homeDirectory.setSelected(true); + break; + case CUSTOM: + customDirectory.setSelected(true); + break; + } + } + + /** + * Sets whether it is possible to select the “next to JAR file” option for + * the configuration location. + * + * @param nextToJarFile + * {@code true} if the configuration file can be saved next to + * the JAR file, {@code false} otherwise + */ + public void setHasNextToJarConfiguration(boolean nextToJarFile) { + this.nextToJarFile.setEnabled(nextToJarFile); + } + + /** + * Sets whether it is possible to select the “custom location” option for + * the configuration location. + * + * @param customDirectory + * {@code true} if the configuration file can be saved to a + * custom location, {@code false} otherwise + */ + public void setHasCustomConfiguration(boolean customDirectory) { + this.customDirectory.setEnabled(customDirectory); + } + + /** + * Returns whether to use the “early encode“ flag for the insert. + * + * @return {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public boolean useEarlyEncode() { + return useEarlyEncode; + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + useEarlyEncodeCheckBox.setSelected(useEarlyEncode); + } + + /** + * Returns the configured insert priority. + * + * @return The insert priority + */ + public PriorityClass getPriority() { + return priority; + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + insertPriorityComboBox.setSelectedItem(priority); + } + + /** + * Returns the selected manifest putter. + * + * @return The selected manifest putter + */ + public ManifestPutter getManifestPutter() { + return (ManifestPutter) manifestPutterComboBox.getSelectedItem(); + } + + /** + * Sets the manifest putter. + * + * @param manifestPutter + * The manifest putter + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + manifestPutterComboBox.setSelectedItem(manifestPutter); + } + + /** * {@inheritDoc} */ @Override @@ -179,6 +339,41 @@ public class PreferencesPage extends TWizardPage { chooseTempDirectory(); } }; + nextToJarFileAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.jar")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionevent) { + configurationLocation = ConfigurationLocation.NEXT_TO_JAR_FILE; + } + }; + homeDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.home")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionevent) { + configurationLocation = ConfigurationLocation.HOME_DIRECTORY; + } + }; + customDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.custom")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + configurationLocation = ConfigurationLocation.CUSTOM; + } + }; + useEarlyEncodeAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.use-early-encode")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + useEarlyEncode = useEarlyEncodeCheckBox.isSelected(); + } + }; + priorityAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.priority")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + priority = (PriorityClass) insertPriorityComboBox.getSelectedItem(); + } + }; I18nContainer.getInstance().registerRunnable(new Runnable() { @@ -187,6 +382,10 @@ public class PreferencesPage extends TWizardPage { selectDefaultTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.default")); selectCustomTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.custom")); chooseTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.choose")); + nextToJarFileAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.jar")); + homeDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.home")); + customDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.custom")); + useEarlyEncodeAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.insert-options.use-early-encode")); } }); } @@ -197,19 +396,17 @@ public class PreferencesPage extends TWizardPage { * @return The preferences panel */ private JPanel createPreferencesPanel() { - JPanel preferencesPanel = new JPanel(new BorderLayout(12, 12)); - - JPanel tempDirectoryPanel = new JPanel(new GridBagLayout()); - preferencesPanel.add(tempDirectoryPanel, BorderLayout.CENTER); + JPanel preferencesPanel = new JPanel(new GridBagLayout()); + preferencesPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); final JLabel tempDirectoryLabel = new JLabel("" + I18n.getMessage("jsite.preferences.temp-directory") + ""); - tempDirectoryPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + preferencesPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); defaultTempDirectory = new JRadioButton(selectDefaultTempDirectoryAction); - tempDirectoryPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0)); + preferencesPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0)); customTempDirectory = new JRadioButton(selectCustomTempDirectoryAction); - tempDirectoryPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); + preferencesPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); ButtonGroup tempDirectoryButtonGroup = new ButtonGroup(); defaultTempDirectory.getModel().setGroup(tempDirectoryButtonGroup); @@ -224,10 +421,46 @@ public class PreferencesPage extends TWizardPage { defaultTempDirectory.setSelected(true); } chooseTempDirectoryAction.setEnabled(tempDirectory != null); - tempDirectoryPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + preferencesPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); JButton chooseButton = new JButton(chooseTempDirectoryAction); - tempDirectoryPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + preferencesPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + + final JLabel configurationDirectoryLabel = new JLabel("" + I18n.getMessage("jsite.preferences.config-directory") + ""); + preferencesPanel.add(configurationDirectoryLabel, new GridBagConstraints(0, 3, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(12, 0, 0, 0), 0, 0)); + + nextToJarFile = new JRadioButton(nextToJarFileAction); + preferencesPanel.add(nextToJarFile, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0)); + + homeDirectory = new JRadioButton(homeDirectoryAction); + preferencesPanel.add(homeDirectory, new GridBagConstraints(0, 5, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); + + customDirectory = new JRadioButton(customDirectoryAction); + preferencesPanel.add(customDirectory, new GridBagConstraints(0, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); + + ButtonGroup configurationDirectoryButtonGroup = new ButtonGroup(); + configurationDirectoryButtonGroup.add(nextToJarFile); + configurationDirectoryButtonGroup.add(homeDirectory); + configurationDirectoryButtonGroup.add(customDirectory); + + final JLabel insertOptionsLabel = new JLabel("" + I18n.getMessage("jsite.preferences.insert-options") + ""); + preferencesPanel.add(insertOptionsLabel, new GridBagConstraints(0, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); + + useEarlyEncodeCheckBox = new JCheckBox(useEarlyEncodeAction); + preferencesPanel.add(useEarlyEncodeCheckBox, new GridBagConstraints(0, 8, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + final JLabel insertPriorityLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.priority")); + preferencesPanel.add(insertPriorityLabel, new GridBagConstraints(0, 9, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + insertPriorityComboBox = new JComboBox(new PriorityClass[] { PriorityClass.MINIMUM, PriorityClass.PREFETCH, PriorityClass.BULK, PriorityClass.UPDATABLE, PriorityClass.SEMI_INTERACTIVE, PriorityClass.INTERACTIVE, PriorityClass.MAXIMUM }); + insertPriorityComboBox.setAction(priorityAction); + preferencesPanel.add(insertPriorityComboBox, new GridBagConstraints(1, 9, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0)); + + final JLabel manifestPutterLabel = new JLabel(I18n.getMessage("jsite.preferences.insert-options.manifest-putter")); + preferencesPanel.add(manifestPutterLabel, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + manifestPutterComboBox = new JComboBox(ManifestPutter.values()); + preferencesPanel.add(manifestPutterComboBox, new GridBagConstraints(1, 10, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 18, 0, 0), 0, 0)); I18nContainer.getInstance().registerRunnable(new Runnable() { @@ -236,6 +469,10 @@ public class PreferencesPage extends TWizardPage { */ public void run() { tempDirectoryLabel.setText("" + I18n.getMessage("jsite.preferences.temp-directory") + ""); + configurationDirectoryLabel.setText("" + I18n.getMessage("jsite.preferences.config-directory") + ""); + insertOptionsLabel.setText("" + I18n.getMessage("jsite.preferences.insert-options") + ""); + insertPriorityLabel.setText(I18n.getMessage("jsite.preferences.insert-options.priority")); + manifestPutterLabel.setText(I18n.getMessage("jsite.preferences.insert-options.manifest-putter")); } }); diff --git a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/de/todesbaum/jsite/gui/ProjectFilesPage.java index fd9ae2f..80bb52c 100644 --- a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java +++ b/src/de/todesbaum/jsite/gui/ProjectFilesPage.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - ProjectFilesPage.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +20,6 @@ package de.todesbaum.jsite.gui; import java.awt.BorderLayout; import java.awt.Dimension; -import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -29,8 +27,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -38,7 +34,6 @@ import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -48,14 +43,9 @@ import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.ListSelectionModel; -import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; -import javax.swing.border.EmptyBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; @@ -65,6 +55,7 @@ import javax.swing.text.Document; import de.todesbaum.jsite.application.FileOption; import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.gui.FileScanner.ScannedFile; import de.todesbaum.jsite.i18n.I18n; import de.todesbaum.jsite.i18n.I18nContainer; import de.todesbaum.util.mime.DefaultMIMETypes; @@ -77,7 +68,7 @@ import de.todesbaum.util.swing.TWizardPage; * * @author David ‘Bombe’ Roden <bombe@freenetproject.org> */ -public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener, ChangeListener { +public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener { /** The project. */ private Project project; @@ -85,15 +76,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis /** The “scan files” action. */ private Action scanAction; - /** The “edit container” action. */ - private Action editContainerAction; - - /** The “add container” action. */ - private Action addContainerAction; - - /** The “delete container” action. */ - protected Action deleteContainerAction; - /** The “ignore hidden files” checkbox. */ private JCheckBox ignoreHiddenFilesCheckBox; @@ -106,6 +88,9 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis /** The “insert” checkbox. */ private JCheckBox fileOptionsInsertCheckBox; + /** The “force insert” checkbox. */ + private JCheckBox fileOptionsForceInsertCheckBox; + /** The “insert redirect” checkbox. */ private JCheckBox fileOptionsInsertRedirectCheckBox; @@ -121,18 +106,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis /** The “mime type” combo box. */ private JComboBox fileOptionsMIMETypeComboBox; - /** The “mime type” combo box model. */ - private DefaultComboBoxModel containerComboBoxModel; - - /** The “container” combo box. */ - private JComboBox fileOptionsContainerComboBox; - - /** The “edition replacement range” spinner. */ - private JSpinner replaceEditionRangeSpinner; - - /** The “replacement” check box. */ - private JCheckBox replacementCheckBox; - /** * Creates a new project file page. * @@ -167,48 +140,12 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis scanAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S); scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); - addContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.add-container")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionAddContainer(); - } - }; - addContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.add-container.tooltip")); - addContainerAction.setEnabled(false); - - editContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.edit-container")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionEditContainer(); - } - }; - editContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.edit-container.tooltip")); - editContainerAction.setEnabled(false); - - deleteContainerAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.delete-container")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionDeleteContainer(); - } - }; - deleteContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.delete-container.tooltip")); - deleteContainerAction.setEnabled(false); - I18nContainer.getInstance().registerRunnable(new Runnable() { @SuppressWarnings("synthetic-access") public void run() { scanAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.rescan")); scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); - addContainerAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.add-container")); - addContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.add-container.tooltip")); - editContainerAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.edit-container")); - editContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.edit-container.tooltip")); - deleteContainerAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.delete-container")); - deleteContainerAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.delete-container.tooltip")); } }); } @@ -272,6 +209,15 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsPanel.add(fileOptionsInsertCheckBox, new GridBagConstraints(0, 4, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsForceInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.force-insert")); + fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); + fileOptionsForceInsertCheckBox.setName("force-insert"); + fileOptionsForceInsertCheckBox.setMnemonic(KeyEvent.VK_F); + fileOptionsForceInsertCheckBox.addActionListener(this); + fileOptionsForceInsertCheckBox.setEnabled(false); + + fileOptionsPanel.add(fileOptionsForceInsertCheckBox, new GridBagConstraints(0, 5, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsCustomKeyTextField = new JTextField(45); fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); fileOptionsCustomKeyTextField.setEnabled(false); @@ -285,9 +231,9 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsInsertRedirectCheckBox.setEnabled(false); final TLabel customKeyLabel = new TLabel(I18n.getMessage("jsite.project-files.custom-key") + ":", KeyEvent.VK_K, fileOptionsCustomKeyTextField); - fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(customKeyLabel, new GridBagConstraints(1, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 5, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(customKeyLabel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); fileOptionsRenameCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.rename"), false); fileOptionsRenameCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.rename.tooltip")); @@ -303,6 +249,10 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis @SuppressWarnings("synthetic-access") private void storeText(DocumentEvent documentEvent) { FileOption fileOption = getSelectedFile(); + if (fileOption == null) { + /* no file selected. */ + return; + } Document document = documentEvent.getDocument(); int documentLength = document.getLength(); try { @@ -326,8 +276,8 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis }); - fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 6, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); fileOptionsMIMETypeComboBox = new JComboBox(DefaultMIMETypes.getAllMIMETypes()); fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); @@ -337,54 +287,8 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsMIMETypeComboBox.setEnabled(false); final TLabel mimeTypeLabel = new TLabel(I18n.getMessage("jsite.project-files.mime-type") + ":", KeyEvent.VK_M, fileOptionsMIMETypeComboBox); - fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsMIMETypeComboBox, new GridBagConstraints(1, 7, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - containerComboBoxModel = new DefaultComboBoxModel(); - fileOptionsContainerComboBox = new JComboBox(containerComboBoxModel); - fileOptionsContainerComboBox.setToolTipText(I18n.getMessage("jsite.project-files.container.tooltip")); - fileOptionsContainerComboBox.setName("project-files.container"); - fileOptionsContainerComboBox.addActionListener(this); - fileOptionsContainerComboBox.setEnabled(false); - fileOptionsContainerComboBox.setVisible(false); - - final TLabel containerLabel = new TLabel(I18n.getMessage("jsite.project-files.container") + ":", KeyEvent.VK_C, fileOptionsContainerComboBox); - containerLabel.setVisible(false); - JButton addContainerButton = new JButton(addContainerAction); - addContainerButton.setVisible(false); - JButton editContainerButton = new JButton(editContainerAction); - editContainerButton.setVisible(false); - JButton deleteContainerButton = new JButton(deleteContainerAction); - deleteContainerButton.setVisible(false); - fileOptionsPanel.add(containerLabel, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsContainerComboBox, new GridBagConstraints(1, 8, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - fileOptionsPanel.add(addContainerButton, new GridBagConstraints(2, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - fileOptionsPanel.add(editContainerButton, new GridBagConstraints(3, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - fileOptionsPanel.add(deleteContainerButton, new GridBagConstraints(4, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - JPanel fileOptionsReplacementPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 6, 6)); - fileOptionsReplacementPanel.setBorder(new EmptyBorder(-6, -6, -6, -6)); - - replacementCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.replacement")); - replacementCheckBox.setName("project-files.replace-edition"); - replacementCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.replacement.tooltip")); - replacementCheckBox.addActionListener(this); - replacementCheckBox.setEnabled(false); - replacementCheckBox.setVisible(false); - fileOptionsReplacementPanel.add(replacementCheckBox); - - replaceEditionRangeSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); - replaceEditionRangeSpinner.setName("project-files.replace-edition-range"); - replaceEditionRangeSpinner.setToolTipText(I18n.getMessage("jsite.project-files.replacement.edition-range.tooltip")); - replaceEditionRangeSpinner.addChangeListener(this); - replaceEditionRangeSpinner.setEnabled(false); - replaceEditionRangeSpinner.setVisible(false); - final JLabel editionRangeLabel = new JLabel(I18n.getMessage("jsite.project-files.replacement.edition-range")); - editionRangeLabel.setVisible(false); - fileOptionsReplacementPanel.add(editionRangeLabel); - fileOptionsReplacementPanel.add(replaceEditionRangeSpinner); - - fileOptionsPanel.add(fileOptionsReplacementPanel, new GridBagConstraints(0, 9, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsMIMETypeComboBox, new GridBagConstraints(1, 8, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); I18nContainer.getInstance().registerRunnable(new Runnable() { @@ -397,6 +301,8 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip")); fileOptionsInsertCheckBox.setText(I18n.getMessage("jsite.project-files.insert")); fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip")); + fileOptionsForceInsertCheckBox.setText(I18n.getMessage("jsite.project-files.force-insert")); + fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); fileOptionsInsertRedirectCheckBox.setText(I18n.getMessage("jsite.project-files.insert-redirect")); fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip")); fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); @@ -405,12 +311,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsRenameCheckBox.setToolTipText("jsite.project-files.rename.tooltip"); fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); mimeTypeLabel.setText(I18n.getMessage("jsite.project-files.mime-type") + ":"); - fileOptionsContainerComboBox.setToolTipText(I18n.getMessage("jsite.project-files.container.tooltip")); - containerLabel.setText(I18n.getMessage("jsite.project-files.container") + ":"); - replacementCheckBox.setText(I18n.getMessage("jsite.project-files.replacement")); - replacementCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.replacement.tooltip")); - replaceEditionRangeSpinner.setToolTipText(I18n.getMessage("jsite.project-files.replacement.edition-range.tooltip")); - editionRangeLabel.setText(I18n.getMessage("jsite.project-files.replacement.edition-range")); } }); @@ -437,42 +337,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis }); } - /** - * Returns a list of all project files. - * - * @return All project files - */ - private List getProjectFiles() { - List files = new ArrayList(); - for (int index = 0, size = projectFileList.getModel().getSize(); index < size; index++) { - files.add((String) projectFileList.getModel().getElementAt(index)); - } - return files; - } - - /** - * Updates the container combo box model. - */ - private void rebuildContainerComboBox() { - /* scan files for containers */ - List files = getProjectFiles(); - List containers = new ArrayList(); // ComboBoxModel - // sucks. No - // contains()! - containers.add(""); - for (String filename : files) { - String container = project.getFileOption(filename).getContainer(); - if (!containers.contains(container)) { - containers.add(container); - } - } - Collections.sort(containers); - containerComboBoxModel.removeAllElements(); - for (String container : containers) { - containerComboBoxModel.addElement(container); - } - } - // // ACTIONS // @@ -494,66 +358,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis } /** - * Adds a container. - */ - private void actionAddContainer() { - String containerName = JOptionPane.showInputDialog(wizard, I18n.getMessage("jsite.project-files.action.add-container.message") + ":", null, JOptionPane.INFORMATION_MESSAGE); - if (containerName == null) { - return; - } - containerName = containerName.trim(); - String filename = (String) projectFileList.getSelectedValue(); - FileOption fileOption = project.getFileOption(filename); - fileOption.setContainer(containerName); - rebuildContainerComboBox(); - fileOptionsContainerComboBox.setSelectedItem(containerName); - } - - /** - * Edits the container. - */ - private void actionEditContainer() { - String selectedFilename = (String) projectFileList.getSelectedValue(); - FileOption fileOption = project.getFileOption(selectedFilename); - String oldContainerName = fileOption.getContainer(); - String containerName = JOptionPane.showInputDialog(wizard, I18n.getMessage("jsite.project-files.action.edit-container.message") + ":", oldContainerName); - if (containerName == null) { - return; - } - if (containerName.equals("")) { - fileOption.setContainer(""); - fileOptionsContainerComboBox.setSelectedItem(""); - return; - } - List files = getProjectFiles(); - for (String filename : files) { - fileOption = project.getFileOption(filename); - if (fileOption.getContainer().equals(oldContainerName)) { - fileOption.setContainer(containerName); - } - } - rebuildContainerComboBox(); - fileOptionsContainerComboBox.setSelectedItem(containerName); - } - - /** - * Deletes the container. - */ - private void actionDeleteContainer() { - if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.project-files.action.delete-container.message"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { - String containerName = (String) fileOptionsContainerComboBox.getSelectedItem(); - List files = getProjectFiles(); - for (String filename : files) { - FileOption fileOption = project.getFileOption(filename); - if (fileOption.getContainer().equals(containerName)) { - fileOption.setContainer(""); - } - } - fileOptionsContainerComboBox.setSelectedItem(""); - } - } - - /** * {@inheritDoc} *

* Updates the file list. @@ -561,21 +365,27 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis public void fileScannerFinished(FileScanner fileScanner) { final boolean error = fileScanner.isError(); if (!error) { - final List files = fileScanner.getFiles(); + final List files = fileScanner.getFiles(); SwingUtilities.invokeLater(new Runnable() { @SuppressWarnings("synthetic-access") public void run() { - projectFileList.setListData(files.toArray(new String[files.size()])); + projectFileList.setListData(files.toArray()); projectFileList.clearSelection(); - rebuildContainerComboBox(); } }); Set entriesToRemove = new HashSet(); Iterator filenames = new HashSet(project.getFileOptions().keySet()).iterator(); while (filenames.hasNext()) { String filename = filenames.next(); - if (!files.contains(filename)) { + boolean found = false; + for (ScannedFile scannedFile : files) { + if (scannedFile.getFilename().equals(filename)) { + found = true; + break; + } + } + if (!found) { entriesToRemove.add(filename); } } @@ -604,11 +414,11 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis * no file is selected */ private FileOption getSelectedFile() { - String filename = (String) projectFileList.getSelectedValue(); - if (filename == null) { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { return null; } - return project.getFileOption(filename); + return project.getFileOption(scannedFile.getFilename()); } // @@ -625,26 +435,34 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis actionScan(); return; } - String filename = (String) projectFileList.getSelectedValue(); - if (filename == null) { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { return; } + String filename = scannedFile.getFilename(); FileOption fileOption = project.getFileOption(filename); if (source instanceof JCheckBox) { JCheckBox checkBox = (JCheckBox) source; if ("default-file".equals(checkBox.getName())) { if (checkBox.isSelected()) { - project.setIndexFile(filename); + if (filename.indexOf('/') > -1) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.invalid-default-file"), null, JOptionPane.ERROR_MESSAGE); + checkBox.setSelected(false); + } else { + project.setIndexFile(filename); + } } else { - project.setIndexFile(null); + if (filename.equals(project.getIndexFile())) { + project.setIndexFile(null); + } } } else if ("insert".equals(checkBox.getName())) { boolean isInsert = checkBox.isSelected(); fileOption.setInsert(isInsert); - if (!isInsert) { - fileOptionsContainerComboBox.setSelectedItem(""); - } fileOptionsInsertRedirectCheckBox.setEnabled(!isInsert); + } else if ("force-insert".equals(checkBox.getName())) { + boolean isForceInsert = checkBox.isSelected(); + fileOption.setForceInsert(isForceInsert); } else if ("insert-redirect".equals(checkBox.getName())) { boolean isInsertRedirect = checkBox.isSelected(); fileOption.setInsertRedirect(isInsertRedirect); @@ -653,24 +471,11 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis boolean isRenamed = checkBox.isSelected(); fileOptionsRenameTextField.setEnabled(isRenamed); fileOption.setChangedName(isRenamed ? fileOptionsRenameTextField.getText() : ""); - } else if ("project-files.replace-edition".equals(checkBox.getName())) { - boolean replaceEdition = checkBox.isSelected(); - fileOption.setReplaceEdition(replaceEdition); - replaceEditionRangeSpinner.setEnabled(replaceEdition); } } else if (source instanceof JComboBox) { JComboBox comboBox = (JComboBox) source; if ("project-files.mime-type".equals(comboBox.getName())) { fileOption.setMimeType((String) comboBox.getSelectedItem()); - } else if ("project-files.container".equals(comboBox.getName())) { - String containerName = (String) comboBox.getSelectedItem(); - fileOption.setContainer(containerName); - boolean enabled = !"".equals(containerName); - editContainerAction.setEnabled(enabled); - deleteContainerAction.setEnabled(enabled); - if (enabled) { - fileOptionsInsertCheckBox.setSelected(true); - } } } } @@ -682,23 +487,21 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis /** * {@inheritDoc} */ + @SuppressWarnings("null") public void valueChanged(ListSelectionEvent e) { - String filename = (String) projectFileList.getSelectedValue(); - boolean enabled = filename != null; - boolean insert = fileOptionsInsertCheckBox.isSelected(); + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + boolean enabled = scannedFile != null; + String filename = (scannedFile == null) ? null : scannedFile.getFilename(); defaultFileCheckBox.setEnabled(enabled); fileOptionsInsertCheckBox.setEnabled(enabled); fileOptionsRenameCheckBox.setEnabled(enabled); fileOptionsMIMETypeComboBox.setEnabled(enabled); - fileOptionsContainerComboBox.setEnabled(enabled); - addContainerAction.setEnabled(enabled); - editContainerAction.setEnabled(enabled); - deleteContainerAction.setEnabled(enabled); - replacementCheckBox.setEnabled(enabled && insert); if (filename != null) { FileOption fileOption = project.getFileOption(filename); defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile())); fileOptionsInsertCheckBox.setSelected(fileOption.isInsert()); + fileOptionsForceInsertCheckBox.setEnabled(scannedFile.getHash().equals(fileOption.getLastInsertHash())); + fileOptionsForceInsertCheckBox.setSelected(fileOption.isForceInsert()); fileOptionsInsertRedirectCheckBox.setEnabled(!fileOption.isInsert()); fileOptionsInsertRedirectCheckBox.setSelected(fileOption.isInsertRedirect()); fileOptionsCustomKeyTextField.setEnabled(fileOption.isInsertRedirect()); @@ -707,13 +510,11 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsRenameTextField.setEnabled(fileOption.hasChangedName()); fileOptionsRenameTextField.setText(fileOption.getChangedName()); fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType()); - fileOptionsContainerComboBox.setSelectedItem(fileOption.getContainer()); - replacementCheckBox.setSelected(fileOption.getReplaceEdition()); - replaceEditionRangeSpinner.setValue(fileOption.getEditionRange()); - replaceEditionRangeSpinner.setEnabled(fileOption.getReplaceEdition()); } else { defaultFileCheckBox.setSelected(false); fileOptionsInsertCheckBox.setSelected(true); + fileOptionsForceInsertCheckBox.setEnabled(false); + fileOptionsForceInsertCheckBox.setSelected(false); fileOptionsInsertRedirectCheckBox.setEnabled(false); fileOptionsInsertRedirectCheckBox.setSelected(false); fileOptionsCustomKeyTextField.setEnabled(false); @@ -723,9 +524,6 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis fileOptionsRenameTextField.setEnabled(false); fileOptionsRenameTextField.setText(""); fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE); - fileOptionsContainerComboBox.setSelectedItem(""); - replacementCheckBox.setSelected(false); - replaceEditionRangeSpinner.setValue(0); } } @@ -741,11 +539,11 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis * The document event to process */ private void processDocumentUpdate(DocumentEvent documentEvent) { - String filename = (String) projectFileList.getSelectedValue(); - if (filename == null) { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { return; } - FileOption fileOption = project.getFileOption(filename); + FileOption fileOption = project.getFileOption(scannedFile.getFilename()); Document document = documentEvent.getDocument(); try { String text = document.getText(0, document.getLength()); @@ -776,24 +574,4 @@ public class ProjectFilesPage extends TWizardPage implements ActionListener, Lis processDocumentUpdate(documentEvent); } - // - // INTERFACE ChangeListener - // - - /** - * {@inheritDoc} - */ - public void stateChanged(ChangeEvent changeEvent) { - String filename = (String) projectFileList.getSelectedValue(); - if (filename == null) { - return; - } - FileOption fileOption = project.getFileOption(filename); - Object source = changeEvent.getSource(); - if (source instanceof JSpinner) { - JSpinner spinner = (JSpinner) source; - fileOption.setEditionRange((Integer) spinner.getValue()); - } - } - } diff --git a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/de/todesbaum/jsite/gui/ProjectInsertPage.java index 2e9286e..bfd8b89 100644 --- a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java +++ b/src/de/todesbaum/jsite/gui/ProjectInsertPage.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - ProjectInsertPage.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,6 +54,9 @@ import de.todesbaum.jsite.application.Project; import de.todesbaum.jsite.application.ProjectInserter; import de.todesbaum.jsite.i18n.I18n; import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.PriorityClass; +import de.todesbaum.util.io.StreamCopier.ProgressListener; import de.todesbaum.util.swing.TWizard; import de.todesbaum.util.swing.TWizardPage; @@ -220,7 +222,27 @@ public class ProjectInsertPage extends TWizardPage implements InsertListener, Cl progressBar.setValue(0); progressBar.setString(I18n.getMessage("jsite.insert.starting")); progressBar.setFont(progressBar.getFont().deriveFont(Font.PLAIN)); - projectInserter.start(); + projectInserter.start(new ProgressListener() { + + public void onProgress(final long copied, final long length) { + SwingUtilities.invokeLater(new Runnable() { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void run() { + int divisor = 1; + while (((copied / divisor) > Integer.MAX_VALUE) || ((length / divisor) > Integer.MAX_VALUE)) { + divisor *= 10; + } + progressBar.setMaximum((int) (length / divisor)); + progressBar.setValue((int) (copied / divisor)); + progressBar.setString("Uploaded: " + copied + " / " + length); + } + }); + } + }); } /** @@ -292,6 +314,38 @@ public class ProjectInsertPage extends TWizardPage implements InsertListener, Cl return uriCopied; } + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + projectInserter.setUseEarlyEncode(useEarlyEncode); + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + projectInserter.setPriority(priority); + } + + /** + * Sets the manifest putter to use for the insert. + * + * @see ProjectInserter#setManifestPutter(ManifestPutter) + * @param manifestPutter + * The manifest putter + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + projectInserter.setManifestPutter(manifestPutter); + } + // // INTERFACE InsertListener // @@ -315,6 +369,14 @@ public class ProjectInsertPage extends TWizardPage implements InsertListener, Cl */ public void projectUploadFinished(Project project) { startTime = System.currentTimeMillis(); + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + progressBar.setString(I18n.getMessage("jsite.insert.starting")); + progressBar.setValue(0); + } + }); } /** @@ -359,6 +421,9 @@ public class ProjectInsertPage extends TWizardPage implements InsertListener, Cl @SuppressWarnings("synthetic-access") public void run() { + if (total == 0) { + return; + } progressBar.setMaximum(total); progressBar.setValue(succeeded + failed + fatal); int progress = (succeeded + failed + fatal) * 100 / total; diff --git a/src/de/todesbaum/jsite/gui/ProjectPage.java b/src/de/todesbaum/jsite/gui/ProjectPage.java index 30d0511..98ad847 100644 --- a/src/de/todesbaum/jsite/gui/ProjectPage.java +++ b/src/de/todesbaum/jsite/gui/ProjectPage.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - ProjectPage.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -609,8 +608,13 @@ public class ProjectPage extends TWizardPage implements ListSelectionListener, D keyDialog.setPublicKey(selectedProject.getRequestURI()); keyDialog.setVisible(true); if (!keyDialog.wasCancelled()) { + String originalPublicKey = selectedProject.getRequestURI(); + String originalPrivateKey = selectedProject.getInsertURI(); selectedProject.setInsertURI(keyDialog.getPrivateKey()); selectedProject.setRequestURI(keyDialog.getPublicKey()); + if (!originalPublicKey.equals(selectedProject.getRequestURI()) || !originalPrivateKey.equals(selectedProject.getInsertURI())) { + selectedProject.setEdition(-1); + } updateCompleteURI(); } } diff --git a/src/de/todesbaum/jsite/i18n/I18n.java b/src/de/todesbaum/jsite/i18n/I18n.java index 5b16aff..4c1cceb 100644 --- a/src/de/todesbaum/jsite/i18n/I18n.java +++ b/src/de/todesbaum/jsite/i18n/I18n.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - I18n.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/jsite/i18n/I18nContainer.java b/src/de/todesbaum/jsite/i18n/I18nContainer.java index 3676c41..da2e0c3 100644 --- a/src/de/todesbaum/jsite/i18n/I18nContainer.java +++ b/src/de/todesbaum/jsite/i18n/I18nContainer.java @@ -1,5 +1,5 @@ /* - * jSite-remote - I18nContainer.java Copyright © 2007 David Roden + * jSite - I18nContainer.java - Copyright © 2007–2012 David Roden * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software diff --git a/src/de/todesbaum/jsite/i18n/jSite.properties b/src/de/todesbaum/jsite/i18n/jSite.properties index 5b1dc7f..9e93e5d 100644 --- a/src/de/todesbaum/jsite/i18n/jSite.properties +++ b/src/de/todesbaum/jsite/i18n/jSite.properties @@ -1,6 +1,5 @@ # -# jSite - a tool for uploading websites into Freenet -# Copyright (C) 2006 David Roden +# jSite - jSite.properties - Copyright © 2006–2012 David Roden # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -27,9 +26,6 @@ # lines single quotes (ASCII 39) needs to be escaped by entering them twice, # otherwise the placeholder will not be replaced! -jsite.main.already-running=jSite is already running

A lock file has been found that suggests that another
instance of jSite is already running. Running multiple instances
of jSite is guaranteed to break your configuration. -jsite.main.already-running.override=Start anyway - jsite.general.ok=OK jsite.general.cancel=Cancel @@ -38,6 +34,9 @@ jsite.wizard.next=Next jsite.wizard.quit=Quit jsite.quit.question=Do you really want to quit? +jsite.quit.question.title=Really quit? +jsite.quit.overwrite-configuration=Overwrite configuration?

A configuration file already exists:
{0}

Should it be overwritten? +jsite.quit.overwrite-configuration.title=Overwrite configuration? jsite.quit.config-not-saved=Configuration not saved

The configuration could not be saved.
Do you want to quit anyway? jsite.menu.languages=Languages @@ -54,7 +53,7 @@ jsite.menu.help=Help jsite.menu.help.check-for-updates=Check for Updates jsite.menu.help.about=About -jsite.about.message=jSite {0}

Copyright \u00a9 2006-2010 David Roden
Released under the GNU General Public License +jsite.about.message=jSite {0}

Copyright \u00a9 2006\u20132012 David Roden
Released under the GNU General Public License jsite.node-manager.heading=Node Manager jsite.node-manager.description=Manage your nodes here. @@ -74,6 +73,14 @@ jsite.preferences.temp-directory.default=Default (chosen by system) jsite.preferences.temp-directory.custom=Custom jsite.preferences.temp-directory.choose=Choose jsite.preferences.temp-directory.choose.approve=Choose +jsite.preferences.config-directory=Location of configuration file +jsite.preferences.config-directory.jar=Next to the JAR file +jsite.preferences.config-directory.home=Home directory +jsite.preferences.config-directory.custom=Custom directory +jsite.preferences.insert-options=Insert options +jsite.preferences.insert-options.use-early-encode=Generate final URI early +jsite.preferences.insert-options.priority=Priority +jsite.preferences.insert-options.manifest-putter=Manifest Putter jsite.insert.heading=Project insert jsite.insert.description=Please wait while the project is being inserted. @@ -126,7 +133,7 @@ jsite.project.project.path=Freesite Path jsite.project.project.edition=Edition jsite.project.project.uri=URI jsite.project.keygen.io-error=Node communication failure

Communication with the node failed
with the following error message:

{0}

Please make sure that you have entered
the correct host name and port number
on the "Node Settings" page. -jsite.project.warning.generate-new-key=Generate new key?

If you generate a new key, your site will be published
under that new key. Any trust that other users put
in the old key of your site will be gone! +jsite.project.warning.generate-new-key=Generate new key?

If you generate a new key, your site will be published
under that new key. Any trust that other users put
in the old key of your site will be gone!
Also, the edition will be reset. jsite.project.warning.reset-edition=Reset edition?

Resetting the edition can lead to insert failures
and lots of confusion if you have not changed
the path or the keys of the project! jsite.project.warning.use-clipboard-now=URI copied

Please note that it is possible that quitting jSite
now will empty the clipboard. Please use the
copied URI immediately in another window! @@ -134,15 +141,6 @@ jsite.project-files.heading=Project Files jsite.project-files.description=On this page you can specify parameters for the files within the project, such as
externally generated keys or MIME types, if the automatic detection failed. jsite.project-files.action.rescan=Re-scan jsite.project-files.action.rescan.tooltip=Re-scan the project directory for new files -jsite.project-files.action.add-container=Add -jsite.project-files.action.add-container.tooltip=Adds a new container to the project and this file to it -jsite.project-files.action.add-container.message=Enter the name of the new container -jsite.project-files.action.edit-container=Edit -jsite.project-files.action.edit-container.tooltip=Changes the name of the container -jsite.project-files.action.edit-container.message=Enter the new name of the container -jsite.project-files.action.delete-container=Delete -jsite.project-files.action.delete-container.tooltip=Deletes this container -jsite.project-files.action.delete-container.message=Do you really want to delete this container? jsite.project-files.ignore-hidden-files=Ignore hidden files jsite.project-files.ignore-hidden-files.tooltip=When selected, hidden files are not inserted jsite.project-files.file-options=File Options @@ -150,6 +148,8 @@ jsite.project-files.default=Default file jsite.project-files.default.tooltip=Specify that this file is the project\u2019s index file jsite.project-files.insert=Insert jsite.project-files.insert.tooltip=Uncheck if you do not want to insert this file +jsite.project-files.force-insert=Force insert +jsite.project-files.force-insert.tooltip=Forces the insert of this file even it is not modified jsite.project-files.insert-redirect=Redirect jsite.project-files.insert-redirect.tooltip=Check if you want to insert a redirect for this file jsite.project-files.custom-key=Custom key @@ -160,12 +160,9 @@ jsite.project-files.mime-type=MIME type jsite.project-files.mime-type.tooltip=Select the correct MIME type here if the detection failed jsite.project-files.container=Container jsite.project-files.container.tooltip=Selects a container for the current file -jsite.project-files.replacement=Replacements -jsite.project-files.replacement.tooltip=Activates replacements in file -jsite.project-files.replacement.edition-range=Range -jsite.project-files.replacement.edition-range.tooltip=Also replace $[EDITION+1], $[EDITION+2]\u2026 jsite.project-files.scan-error=Error scanning files

Either the directory of the project does not exist
or some files/directories in it are not accessible.
Please go back and select the correct directory. jsite.project-files.insert-now=Insert now +jsite.project-files.invalid-default-file=Only files in the root directory may be selected as default files. jsite.update-checker.found-version.title=Found New Version jsite.update-checker.found-version.message=A new version was found.

Version {0} (released {1,date}) @@ -185,7 +182,6 @@ jsite.key-dialog.label.public-key=Public Key jsite.key-dialog.label.actions=Actions jsite.warning.empty-index=No default file

You did not specify a default file for this project.
While it is possible to insert a project without a default
file you should specify one to ease browsing. -jsite.warning.container-index=Default file in container

Your default file was placed in a container!
This might make other people shun your page. jsite.warning.index-not-html=Default file is not HTML

Your default file does not have the MIME type "text/html"!
Loading your Freesite in a browser may give unexpected results. jsite.error.no-node-selected=No node selected

Please select a node from the menu! diff --git a/src/de/todesbaum/jsite/i18n/jSite_de.properties b/src/de/todesbaum/jsite/i18n/jSite_de.properties index d499982..d6441ca 100644 --- a/src/de/todesbaum/jsite/i18n/jSite_de.properties +++ b/src/de/todesbaum/jsite/i18n/jSite_de.properties @@ -1,6 +1,5 @@ # -# jSite - a tool for uploading websites into Freenet -# Copyright (C) 2006 David Roden +# jSite - jSite_de.properties - Copyright © 2006–2012 David Roden # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -27,9 +26,6 @@ # lines single quotes (ASCII 39) needs to be escaped by entering them twice, # otherwise the placeholder will not be replaced! -jsite.main.already-running=jSite l\u00e4uft bereits!

Es wurde festgestellt, dass jSite bereits l\u00e4uft. Das kann
zu Besch\u00e4digungen an der Konfiguration f\u00fchren. -jsite.main.already-running.override=Trotzdem starten - jsite.general.ok=OK jsite.general.cancel=Abbrechen @@ -38,6 +34,9 @@ jsite.wizard.next=Vorw\u00e4rts jsite.wizard.quit=Beenden jsite.quit.question=M\u00f6chten Sie jSite wirklich beenden? +jsite.quit.question.title=Wirklich beenden? +jsite.quit.overwrite-configuration=Konfiguration \u00fcberschreiben?

Es existiert bereits eine Konfigurationsdatei unter:
{0}

Soll sie \u00fcberschrieben werden? +jsite.quit.overwrite-configuration.title=Konfiguration \u00fcberschreiben? jsite.quit.config-not-saved=Konfiguration nicht gespeichert

Die Konfiguration konnte nicht gespeichert werden.
Soll jSite trotzdem beendet werden? jsite.menu.languages=Sprachen @@ -54,7 +53,7 @@ jsite.menu.help=Hilfe jsite.menu.help.check-for-updates=Auf Updates pr\u00fcfen jsite.menu.help.about=\u00dcber -jsite.about.message=jSite {0}

Copyright \u00a9 2006-2010 David Roden
Ver\u00f6ffentlicht unter der GNU General Public License +jsite.about.message=jSite {0}

Copyright \u00a9 2006\u20132012 David Roden
Ver\u00f6ffentlicht unter der GNU General Public License jsite.node-manager.heading=Nodeverwaltung jsite.node-manager.description=Verwalten Sie hier Ihre Nodes. @@ -74,6 +73,14 @@ jsite.preferences.temp-directory.default=Standard (vom System bestimmt) jsite.preferences.temp-directory.custom=Eigenes jsite.preferences.temp-directory.choose=Ausw\u00e4hlen jsite.preferences.temp-directory.choose.approve=Ausw\u00e4hlen +jsite.preferences.config-directory=Lage der Konfigurationsdatei +jsite.preferences.config-directory.jar=Neben der JAR-Datei +jsite.preferences.config-directory.home=Benutzerverzeichnis +jsite.preferences.config-directory.custom=Angegebenes Verzeichnis +jsite.preferences.insert-options=Einf\u00fcgeoptionen +jsite.preferences.insert-options.use-early-encode=Endg\u00fcltige URI fr\u00fcher berechnen +jsite.preferences.insert-options.priority=Priorit\u00e4t +jsite.preferences.insert-options.manifest-putter=Manifesterstellung jsite.insert.heading=Projekt einf\u00fcgen jsite.insert.description=Bitte warten Sie, w\u00e4hrend das Projekt eingef\u00fcgt wird. @@ -126,7 +133,7 @@ jsite.project.project.path=Seitenpfad jsite.project.project.edition=Edition jsite.project.project.uri=Anfrage-URI jsite.project.keygen.io-error=Kommunikation fehlgeschlagen

Die Kommunikation mit dem Freenet Node
ergab folgende Fehlermeldung:

{0}

Bitte vergewissern Sie sich, dass der Node l\u00e4uft und dass Sie
den korrekten Hostnamen und die korrekte Portnummer auf der
\u201eNode Einstellungen\u201c Seite eingegeben haben. -jsite.project.warning.generate-new-key=Neues Schl\u00fcsselpaar generieren?

Wenn Sie das Schl\u00fcsselpaar f\u00fcr das Projekt \u00e4ndern,
wird sich die URI f\u00fcr Ihr Projekt ebenfalls
\u00e4ndern, und jegliches Vertrauen, dass andere
Benutzer in das alte Schl\u00fcsselpaar hatten, wird
verloren gehen! +jsite.project.warning.generate-new-key=Neues Schl\u00fcsselpaar generieren?

Wenn Sie das Schl\u00fcsselpaar f\u00fcr das Projekt \u00e4ndern,
wird sich die URI f\u00fcr Ihr Projekt ebenfalls
\u00e4ndern, und jegliches Vertrauen, dass andere
Benutzer in das alte Schl\u00fcsselpaar hatten, wird
verloren gehen! Au\u00dferdem wird die Edition zur\u00fcckgesetzt. jsite.project.warning.reset-edition=Edition zur\u00fccksetzen?

Das Zur\u00fccksetzen der Editionsnummer kann zum
Fehlschlagen des Einf\u00fcgens f\u00fchren, wenn sich nicht
auch die URI oder der Pfad des Projekts ge\u00e4ndert haben! jsite.project.warning.use-clipboard-now=Anfrage-URI kopiert

Bitte beachten Sie, dass die Zwischenablage nach dem
Beenden von jSite eventuell nicht mehr die kopierte
URI enth\u00e4lt. Bitte f\u00fcgen Sie sie daher schleunigst in
ein anderes Programm ein! @@ -134,15 +141,6 @@ jsite.project-files.heading=Projektdateien jsite.project-files.description=Auf dieser Seite k\u00f6nnen Parameter f\u00fcr die einzelnen Dateien dieses Projekts angegeben werden, z.B.
extern erstellte Schl\u00fcssel oder der korrekte MIME-Typ, wenn er nicht automatisch richtig erkannt wurde. jsite.project-files.action.rescan=Erneut einlesen jsite.project-files.action.rescan.tooltip=Die Liste mit Dateien dieses Projekts neu einlesen -jsite.project-files.action.add-container=Hinzuf\u00fcgen -jsite.project-files.action.add-container.tooltip=Erzeugt einen neuen Container und f\u00fcgt diese Datei hinzu -jsite.project-files.action.add-container.message=Bitte geben Sie den Namen des neuen Containers an -jsite.project-files.action.edit-container=\u00c4ndern -jsite.project-files.action.edit-container.tooltip=\u00c4ndert den Namen des Containers -jsite.project-files.action.edit-container.message=Bitte geben Sie den neuen Namen des Containers an -jsite.project-files.action.delete-container=L\u00f6schen -jsite.project-files.action.delete-container.tooltip=L\u00f6scht diesen Container -jsite.project-files.action.delete-container.message=Wollen Sie diesen Container wirklich l\u00f6schen? jsite.project-files.ignore-hidden-files=Versteckte Dateien ignorieren jsite.project-files.ignore-hidden-files.tooltip=Verhindert, dass versteckte Dateien hochgeladen werden jsite.project-files.file-options=Dateioptionen @@ -150,6 +148,8 @@ jsite.project-files.default=Index-Datei jsite.project-files.default.tooltip=Lege Index-Datei f\u00fcr Projekt fest jsite.project-files.insert=Einf\u00fcgen jsite.project-files.insert.tooltip=jSite f\u00fcgt diese Datei ein +jsite.project-files.force-insert=Einf\u00fcgen erzwingen +jsite.project-files.force-insert.tooltip=F\u00fcgt diese Datei ein, auch wenn sie nicht modifiziert wurde jsite.project-files.insert-redirect=Umleitung jsite.project-files.insert-redirect.tooltip=F\u00fcgt eine Umleitung ein jsite.project-files.custom-key=Extern erstellter Schl\u00fcssel @@ -160,12 +160,9 @@ jsite.project-files.mime-type=MIME-Typ jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier ausw\u00e4hlen, wenn die automatische Erkennenung falsch ist jsite.project-files.container=Container jsite.project-files.container.tooltip=W\u00e4hlt einen Container f\u00fcr diese Datei aus -jsite.project-files.replacement=Ersetzungen -jsite.project-files.replacement.tooltip=Aktiviert Ersetzungen in Datei -jsite.project-files.replacement.edition-range=Reichweite -jsite.project-files.replacement.edition-range.tooltip=Ersetzt auch $[EDITION+1], $[EDITION+2], usw. jsite.project-files.scan-error=Fehler beim Einlesen der Dateien

Entweder existiert das Projektverzeichnis nicht,
oder einige Dateien und/oder Verzeichnisse sind nicht lesbar!
Bitte gehen Sie zur\u00fcck und beheben Sie den Fehler! jsite.project-files.insert-now=Jetzt einf\u00fcgen +jsite.project-files.invalid-default-file=Nur Dateien im obersten Verzeichnis d\u00fcrfen als Index-Dateien ausgew\u00e4hlt werden. jsite.update-checker.found-version.title=Neue Version gefunden jsite.update-checker.found-version.message=Eine neue Version wurde gefunden.

Version {0} (ver\u00f6ffentlicht {1,date}) @@ -185,7 +182,6 @@ jsite.key-dialog.label.public-key=\u00d6ffentlicher Schl\u00fcssel jsite.key-dialog.label.actions=Aktionen jsite.warning.empty-index=Keine Index-Datei gew\u00e4hlt

Sie haben keine Index-Datei f\u00fcr das Projekt angegeben.
Obwohl es m\u00f6glich ist, das zu machen, sollten Sie doch
eine Index-Datei angeben, um das Browsen zu erleichtern. -jsite.warning.container-index=Index-Datei in Container

Ihre Index-Datei befindet sich in einem Container! Das kann
dazu f\u00fchren, dass Ihre Freesite von anderen Leuten gemieden wird. jsite.warning.index-not-html=Index-Datei ist kein HTML

Ihre Index-Datei hat nicht den MIME-Typ "text/html"!
Das kann beim Besuch Ihrer Freesite zu
unerwarteten Ergebnissen f\u00fchren. jsite.error.no-node-selected=Kein Node ausgew\u00e4hlt

Bitte w\u00e4hlen Sie einen Node aus dem Men\u00fc! diff --git a/src/de/todesbaum/jsite/i18n/jSite_fr.properties b/src/de/todesbaum/jsite/i18n/jSite_fr.properties index 49fcce8..39f9acd 100644 --- a/src/de/todesbaum/jsite/i18n/jSite_fr.properties +++ b/src/de/todesbaum/jsite/i18n/jSite_fr.properties @@ -1,6 +1,5 @@ # -# jSite - a tool for uploading websites into Freenet -# Copyright (C) 2006 David Roden +# jSite - jSite_fr.properties - Copyright © 2006–2012 David Roden # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -27,9 +26,6 @@ # lines single quotes (ASCII 39) needs to be escaped by entering them twice, # otherwise the placeholder will not be replaced! -jsite.main.already-running=jSite est d\u00e9ja lanc\u00e9!

Ne faites pas tourner plusieurs instances
sous peine de perdre vos fichiers de configuration ! -jsite.main.already-running.override=D\u00e9marrer - jsite.general.ok=OK jsite.general.cancel=Annuler @@ -38,6 +34,9 @@ jsite.wizard.next=Suivant jsite.wizard.quit=Quitter jsite.quit.question=Voulez-vous r\u00e9ellement quitter? +jsite.quit.question.title=Souhaitez vous quitter? +jsite.quit.overwrite-configuration=Ecraser la configuration?

Un fichier de configuration éxiste déjà:
{0}

Doit-il être écrasé ? +jsite.quit.overwrite-configuration.title=Ecraser la configuration? jsite.quit.config-not-saved=Configuration non sauvegard\u00e9e

La configuration n'a pas pu \u00eatre sauv\u00e9e.
Voulez vous quitter tout de m\u00eame? jsite.menu.languages=Langue @@ -54,7 +53,7 @@ jsite.menu.help=Aide jsite.menu.help.check-for-updates=Mises \u00e0 jour jsite.menu.help.about=A propos de jSite -jsite.about.message=jSite {0}

Copyright \u00a9 2006-2010 David Roden
Released under the GNU General Public License +jsite.about.message=jSite {0}

Copyright \u00a9 2006\u20132012 David Roden
Publié sous GNU General Public License jsite.node-manager.heading=Gestionnaire de noeud jsite.node-manager.description=G\u00e9rez vos noeuds. @@ -74,6 +73,14 @@ jsite.preferences.temp-directory.default=D\u00e9faut (choix syst\u00e8mes) jsite.preferences.temp-directory.custom=Personnalis\u00e9 jsite.preferences.temp-directory.choose=Choisir jsite.preferences.temp-directory.choose.approve=Choisir +jsite.preferences.config-directory=Chemin du fichier de configuration +jsite.preferences.config-directory.jar=Suivant, le fichier JAR +jsite.preferences.config-directory.home=Acceuil +jsite.preferences.config-directory.custom=Répertoire personnel +jsite.preferences.insert-options=Options d'insertion +jsite.preferences.insert-options.use-early-encode=Générer d'abord l'URI +jsite.preferences.insert-options.priority=Priorité +jsite.preferences.insert-options.manifest-putter=Ajout de Manifest jsite.insert.heading=Projet d'insertion jsite.insert.description=Veuillez attendre durant l'insertion du projet. @@ -83,10 +90,13 @@ jsite.insert.start-time=Commenc\u00e9 \u00e0 jsite.insert.starting=D\u00e9marrage\u2026 jsite.insert.done=Termin\u00e9. jsite.insert.done.title=Insertion effectu\u00e9e +jsite.insert.insert-aborted=L'insertion a été annulée. +jsite.insert.insert-aborted.title=Insertion Annulée jsite.insert.progress=Avancement jsite.insert.k-per-s=Ko/s jsite.insert.insert-failed=Insertion \u00e9chou\u00e9e

L'insertion du projet \u00e0 \u00e9chou\u00e9e.
Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s. jsite.insert.insert-failed-with-cause=Insertion \u00e9chou\u00e9e

L'insertion du projet \u00e0 \u00e9chou\u00e9e.
Certain fichiers n'ont pas \u00e9t\u00e9 ins\u00e9r\u00e9s.
L'erreur suivante s'est produite:

{0} +jsite.insert.insert-failed.title=Insertion Echouée jsite.insert.inserted=Projet ins\u00e9r\u00e9!

Votre projet \u00e0 \u00e9t\u00e9 correctement ins\u00e9r\u00e9. jsite.insert.okay-copy-uri=Copier l'URI vers le presse-papiers jsite.insert.reinserted-edition=Edition r\u00e9ins\u00e9r\u00e9e

L'\u00e9dition que vous \u00eates en train d'ins\u00e9rer
a d\u00e9j\u00e0 \u00e9t\u00e9 ins\u00e9r\u00e9e avant. @@ -131,15 +141,6 @@ jsite.project-files.heading=Fichiers du projet jsite.project-files.description=Dans cette page vous pouvez sp\u00e9cifier les informations concernant la configuration des noeuds telles que:
Le type de contenu mime si l'auto d\u00e9tection \u00e0 \u00e9chou\u00e9e. jsite.project-files.action.rescan=Re-scan jsite.project-files.action.rescan.tooltip=V\u00e9rifier la pr\u00e9sence de nouveau fichiers -jsite.project-files.action.add-container=Ajouter -jsite.project-files.action.add-container.tooltip=Ajouter un nouveau container au projet -jsite.project-files.action.add-container.message=Entrez le nom du container -jsite.project-files.action.edit-container=Editer -jsite.project-files.action.edit-container.tooltip=Changer le nom du container -jsite.project-files.action.edit-container.message=Entrez le nouveau nom du container -jsite.project-files.action.delete-container=Supprimer -jsite.project-files.action.delete-container.tooltip=Supprimer ce container. -jsite.project-files.action.delete-container.message=Voulez vous r\u00e9ellement supprimer ce container? jsite.project-files.ignore-hidden-files=Ignorer les fichiers cach\u00e9s jsite.project-files.ignore-hidden-files.tooltip=Si s\u00e9lectionn\u00e9, les fichiers cach\u00e9s ne sont pas ins\u00e9r\u00e9s jsite.project-files.file-options=Option des fichiers @@ -147,6 +148,8 @@ jsite.project-files.default=Fichier par d\u00e9faut jsite.project-files.default.tooltip=Est-ce l'index? jsite.project-files.insert=Ins\u00e9rer jsite.project-files.insert.tooltip=D\u00e9cochez si vous ne voulez pas ins\u00e9rer ce fichier +jsite.project-files.force-insert=Forcer l'insertion +jsite.project-files.force-insert.tooltip=Forcer l'insertion de ce fichier tant qu'il n'est pas modifié jsite.project-files.insert-redirect=Redirection jsite.project-files.insert-redirect.tooltip=Cochez si vous voulez ins\u00e9rer une redirection pour ce fichier jsite.project-files.custom-key=Clef existante @@ -157,12 +160,9 @@ jsite.project-files.mime-type=MIME type jsite.project-files.mime-type.tooltip=S\u00e9lectionez le type MIME du fichier si la d\u00e9tection \u00e0 \u00e9chou\u00e9e jsite.project-files.container=Container jsite.project-files.container.tooltip=S\u00e9lectionnez un container pour le fichier -jsite.project-files.replacement=Remplacer -jsite.project-files.replacement.tooltip=Activer les remplacements -jsite.project-files.replacement.edition-range=Plage -jsite.project-files.replacement.edition-range.tooltip=Remplacer de $[EDITION+1] \u00e0 $[EDITION+2] jsite.project-files.scan-error=Erreur lors du parcours des fichiers

Soit le r\u00e9pertoire du projet n'existe pas,
ou des fichiers/r\u00e9pertoires sont inaccessibles.
Veuillez revenir en arri\u00e8re et s\u00e9lectionner un autre r\u00e9pertoire. jsite.project-files.insert-now=Ins\u00e9rer +jsite.project-files.invalid-default-file=Seulement les fichiers de la racine peuvent être selectionnés comme fichiers par defaut jsite.update-checker.found-version.title=Nouvelle version disponible jsite.update-checker.found-version.message=Une nouvelle version est disponible.

Version {0} (publi\u00e9e le {1,date}) @@ -182,7 +182,6 @@ jsite.key-dialog.label.public-key=Cl\u00e9 publique jsite.key-dialog.label.actions=Actions jsite.warning.empty-index=Pas de fichier par d\u00e9faut

Avez vous sp\u00e9cifi\u00e9 un fichier par d\u00e9faut pour le projet?
M\u00eame s'il est possible de ne pas en sp\u00e9cifier, c'est g\u00e9n\u00e9ralement une mauvaise id\u00e9e. -jsite.warning.container-index=Fichier principal du container

Votre fichier par d\u00e9faut \u00e0 \u00e9t\u00e9 plac\u00e9 dans un container!
Ceci peut avoir pour effet de cacher cette page aux utilisateurs. jsite.warning.index-not-html=Le fichier principal n'est pas un fichier HTML!

Votre fichier par d\u00e9faut n'est pas du type MIME "text/html"!
Chargez ce type de fichiers dans un navigateur peut \u00eatre dangereux. jsite.error.no-node-selected=Pas de noeud s\u00e9lectionn\u00e9

S\u00e9lectionnez un noeud dans le menu! diff --git a/src/de/todesbaum/jsite/main/CLI.java b/src/de/todesbaum/jsite/main/CLI.java index 03ddb50..65c9253 100644 --- a/src/de/todesbaum/jsite/main/CLI.java +++ b/src/de/todesbaum/jsite/main/CLI.java @@ -1,6 +1,5 @@ /* - * jSite - - * Copyright (C) 2006 David Roden + * jSite - CLI.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +25,7 @@ import de.todesbaum.jsite.application.InsertListener; import de.todesbaum.jsite.application.Node; import de.todesbaum.jsite.application.Project; import de.todesbaum.jsite.application.ProjectInserter; +import de.todesbaum.util.io.StreamCopier.ProgressListener; /** * Command-line interface for jSite. @@ -68,6 +68,7 @@ public class CLI implements InsertListener { if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) { outputWriter.println("\nParameters:\n"); + outputWriter.println(" --config-file="); outputWriter.println(" --node="); outputWriter.println(" --project="); outputWriter.println(" --local-directory="); @@ -79,11 +80,19 @@ public class CLI implements InsertListener { return; } - Configuration configuration = new Configuration(); - if (!configuration.createLockFile()) { - outputWriter.println("Lock file found!"); - return; + String configFile = System.getProperty("user.home") + "/.jSite/config7"; + for (String argument : args) { + String value = argument.substring(argument.indexOf('=') + 1).trim(); + if (argument.startsWith("--config-file=")) { + configFile = value; + } + } + + ConfigurationLocator configurationLocator = new ConfigurationLocator(); + if (configFile != null) { + configurationLocator.setCustomLocation(configFile); } + Configuration configuration = new Configuration(configurationLocator, configurationLocator.findPreferredLocation()); projectInserter.addInsertListener(this); projects = configuration.getProjects(); @@ -97,6 +106,10 @@ public class CLI implements InsertListener { Project currentProject = null; for (String argument : args) { + if (argument.startsWith("--config-file=")) { + /* we already parsed this one. */ + continue; + } String value = argument.substring(argument.indexOf('=') + 1).trim(); if (argument.startsWith("--node=")) { Node newNode = getNode(value); @@ -143,9 +156,11 @@ public class CLI implements InsertListener { } } + int errorCode = 1; if (currentProject != null) { if (insertProject(currentProject)) { outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted."); + errorCode = 0; } else { outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted."); } @@ -153,6 +168,8 @@ public class CLI implements InsertListener { configuration.setProjects(projects); configuration.save(); + + System.exit(errorCode); } /** @@ -201,7 +218,12 @@ public class CLI implements InsertListener { return false; } projectInserter.setProject(currentProject); - projectInserter.start(); + projectInserter.start(new ProgressListener() { + + public void onProgress(long copied, long length) { + System.out.print("Uploaded: " + copied + " / " + length + " bytes...\r"); + } + }); synchronized (lockObject) { while (!finished) { try { @@ -229,7 +251,7 @@ public class CLI implements InsertListener { * {@inheritDoc} */ public void projectUploadFinished(Project project) { - outputWriter.println("Project \"" + project.getName() + "\" has ben uploaded, starting insert..."); + outputWriter.println("Project \"" + project.getName() + "\" has been uploaded, starting insert..."); } /** diff --git a/src/de/todesbaum/jsite/main/Configuration.java b/src/de/todesbaum/jsite/main/Configuration.java index e98a18d..f5001b0 100644 --- a/src/de/todesbaum/jsite/main/Configuration.java +++ b/src/de/todesbaum/jsite/main/Configuration.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - Configuration.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,10 +32,15 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; import de.todesbaum.jsite.application.FileOption; import de.todesbaum.jsite.application.Node; import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.PriorityClass; import de.todesbaum.util.io.Closer; import de.todesbaum.util.io.StreamCopier; import de.todesbaum.util.xml.SimpleXML; @@ -49,112 +53,92 @@ import de.todesbaum.util.xml.XML; */ public class Configuration { - /** The name of the file the configuration is stored to. */ - private String filename; - - /** The name of the lock file. */ - private String lockFilename; - /** The root node of the configuration. */ private SimpleXML rootNode; - /** - * Creates a new configuration with the default name of the configuration - * file. - */ - public Configuration() { - this(System.getProperty("user.home") + "/.jSite/config7"); - } + /** The configuration locator. */ + private final ConfigurationLocator configurationLocator; + + /** Where the configuration resides. */ + private ConfigurationLocation configurationLocation; /** * Creates a new configuration that is read from the given file. * - * @param filename - * The name of the configuration file + * @param configurationLocator + * The configuration locator + * @param configurationLocation + * The configuration directory */ - public Configuration(String filename) { - this(filename, filename + ".lock"); + public Configuration(ConfigurationLocator configurationLocator, ConfigurationLocation configurationLocation) { + this.configurationLocator = configurationLocator; + this.configurationLocation = configurationLocation; + readConfiguration(configurationLocator.getFile(configurationLocation)); } - /** - * Creates a new configuration that is read from the given file and uses the - * given lock file. - * - * @param filename - * The name of the configuration file - * @param lockFilename - * The name of the lock file - */ - public Configuration(String filename, String lockFilename) { - this.filename = filename; - this.lockFilename = lockFilename; - readConfiguration(); - } + // + // ACCESSORS + // /** - * Creates the directory of the configuration file. + * Returns the configuration locator. * - * @return true if the directory exists, or if it could be - * created, false otherwise + * @return The configuration locator */ - private boolean createConfigDirectory() { - File configDirectory = new File(filename).getAbsoluteFile().getParentFile(); - return (configDirectory.exists() && configDirectory.isDirectory()) || configDirectory.mkdirs(); + public ConfigurationLocator getConfigurationLocator() { + return configurationLocator; } /** - * Creates the lock file. + * Returns the location the configuration will be written to when calling + * {@link #save()}. * - * @return true if the lock file did not already exist and - * could be created, false otherwise + * @return The location the configuration will be written to */ - public boolean createLockFile() { - if (!createConfigDirectory()) { - return false; - } - File lockFile = new File(lockFilename); - try { - boolean fileLocked = lockFile.createNewFile(); - if (fileLocked) { - lockFile.deleteOnExit(); - } - return fileLocked; - } catch (IOException e) { - /* ignore. */ - } - return false; + public ConfigurationLocation getConfigurationDirectory() { + return configurationLocation; } /** - * Tells the VM to remove the lock file on program exit. + * Sets the location the configuration will be written to when calling + * {@link #save()}. + * + * @param configurationLocation + * The location to write the configuration to */ - public void removeLockfileOnExit() { - new File(lockFilename).deleteOnExit(); + public void setConfigurationLocation(ConfigurationLocation configurationLocation) { + this.configurationLocation = configurationLocation; } /** * Reads the configuration from the file. + * + * @param filename + * The name of the file to read the configuration from */ - private void readConfiguration() { - File configurationFile = new File(filename); - if (configurationFile.exists()) { - ByteArrayOutputStream fileByteOutputStream = null; - FileInputStream fileInputStream = null; - try { - fileByteOutputStream = new ByteArrayOutputStream(); - fileInputStream = new FileInputStream(configurationFile); - StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length()); - fileByteOutputStream.close(); - byte[] fileBytes = fileByteOutputStream.toByteArray(); - rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes)); - return; - } catch (FileNotFoundException e) { - /* ignore. */ - } catch (IOException e) { - /* ignore. */ - } finally { - Closer.close(fileInputStream); - Closer.close(fileByteOutputStream); + private void readConfiguration(String filename) { + Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Reading configuration from: " + filename); + if (filename != null) { + File configurationFile = new File(filename); + if (configurationFile.exists()) { + ByteArrayOutputStream fileByteOutputStream = null; + FileInputStream fileInputStream = null; + try { + fileByteOutputStream = new ByteArrayOutputStream(); + fileInputStream = new FileInputStream(configurationFile); + StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length()); + fileByteOutputStream.close(); + byte[] fileBytes = fileByteOutputStream.toByteArray(); + rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes)); + return; + } catch (FileNotFoundException e) { + /* ignore. */ + } catch (IOException e) { + /* ignore. */ + } finally { + Closer.close(fileInputStream); + Closer.close(fileByteOutputStream); + } } } rootNode = new SimpleXML("configuration"); @@ -167,7 +151,8 @@ public class Configuration { * false otherwise */ public boolean save() { - File configurationFile = new File(filename); + Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Trying to save configuration to: " + configurationLocation); + File configurationFile = new File(configurationLocator.getFile(configurationLocation)); if (!configurationFile.exists()) { File configurationFilePath = configurationFile.getAbsoluteFile().getParentFile(); if (!configurationFilePath.exists() && !configurationFilePath.mkdirs()) { @@ -332,7 +317,11 @@ public class Configuration { Project project = new Project(); projects.add(project); project.setDescription(projectNode.getNode("description").getValue("")); - project.setIndexFile(projectNode.getNode("index-file").getValue("")); + String indexFile = projectNode.getNode("index-file").getValue(""); + if (indexFile.indexOf('/') > -1) { + indexFile = ""; + } + project.setIndexFile(indexFile); project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue("0"))); project.setLocalPath(projectNode.getNode("local-path").getValue("")); project.setName(projectNode.getNode("name").getValue("")); @@ -348,8 +337,26 @@ public class Configuration { } else { project.setIgnoreHiddenFiles(true); } - SimpleXML fileOptionsNode = projectNode.getNode("file-options"); + + /* load last insert hashes. */ Map fileOptions = new HashMap(); + SimpleXML lastInsertHashesNode = projectNode.getNode("last-insert-hashes"); + if (lastInsertHashesNode != null) { + for (SimpleXML fileNode : lastInsertHashesNode.getNodes("file")) { + String filename = fileNode.getNode("filename").getValue(); + String lastInsertHash = fileNode.getNode("last-insert-hash").getValue(); + int lastInsertEdition = Integer.valueOf(fileNode.getNode("last-insert-edition").getValue()); + String lastInsertFilename = filename; + if (fileNode.getNode("last-insert-filename") != null) { + lastInsertFilename = fileNode.getNode("last-insert-filename").getValue(); + } + FileOption fileOption = project.getFileOption(filename); + fileOption.setLastInsertHash(lastInsertHash).setLastInsertEdition(lastInsertEdition).setLastInsertFilename(lastInsertFilename); + fileOptions.put(filename, fileOption); + } + } + + SimpleXML fileOptionsNode = projectNode.getNode("file-options"); if (fileOptionsNode != null) { SimpleXML[] fileOptionNodes = fileOptionsNode.getNodes("file-option"); for (SimpleXML fileOptionNode : fileOptionNodes) { @@ -364,11 +371,6 @@ public class Configuration { fileOption.setChangedName(fileOptionNode.getNode("changed-name").getValue()); } fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue("")); - fileOption.setContainer(fileOptionNode.getNode("container").getValue()); - if (fileOptionNode.getNode("replace-edition") != null) { - fileOption.setReplaceEdition(Boolean.parseBoolean(fileOptionNode.getNode("replace-edition").getValue())); - fileOption.setEditionRange(Integer.parseInt(fileOptionNode.getNode("edition-range").getValue())); - } fileOptions.put(filename, fileOption); } } @@ -401,6 +403,20 @@ public class Configuration { projectNode.append("insert-uri", project.getInsertURI()); projectNode.append("request-uri", project.getRequestURI()); projectNode.append("ignore-hidden-files", String.valueOf(project.isIgnoreHiddenFiles())); + + /* store last insert hashes. */ + SimpleXML lastInsertHashesNode = projectNode.append("last-insert-hashes"); + for (Entry fileOption : project.getFileOptions().entrySet()) { + if ((fileOption.getValue().getLastInsertHash() == null) || (fileOption.getValue().getLastInsertHash().length() == 0)) { + continue; + } + SimpleXML fileNode = lastInsertHashesNode.append("file"); + fileNode.append("filename", fileOption.getKey()); + fileNode.append("last-insert-hash", fileOption.getValue().getLastInsertHash()); + fileNode.append("last-insert-edition", String.valueOf(fileOption.getValue().getLastInsertEdition())); + fileNode.append("last-insert-filename", fileOption.getValue().getLastInsertFilename()); + } + SimpleXML fileOptionsNode = projectNode.append("file-options"); Iterator> entries = project.getFileOptions().entrySet().iterator(); while (entries.hasNext()) { @@ -414,9 +430,6 @@ public class Configuration { fileOptionNode.append("custom-key", fileOption.getCustomKey()); fileOptionNode.append("changed-name", fileOption.getChangedName()); fileOptionNode.append("mime-type", fileOption.getMimeType()); - fileOptionNode.append("container", fileOption.getContainer()); - fileOptionNode.append("replace-edition", String.valueOf(fileOption.getReplaceEdition())); - fileOptionNode.append("edition-range", String.valueOf(fileOption.getEditionRange())); } } } @@ -564,4 +577,69 @@ public class Configuration { } } + /** + * Returns whether to use the “early encode“ flag for the insert. + * + * @return {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public boolean useEarlyEncode() { + return getNodeBooleanValue(new String[] { "use-early-encode" }, false); + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + * @return This configuration + */ + public Configuration setUseEarlyEncode(boolean useEarlyEncode) { + rootNode.replace("use-early-encode", String.valueOf(useEarlyEncode)); + return this; + } + + /** + * Returns the insert priority. + * + * @return The insert priority + */ + public PriorityClass getPriority() { + return PriorityClass.valueOf(getNodeValue(new String[] { "insert-priority" }, "interactive")); + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + * @return This configuration + */ + public Configuration setPriority(PriorityClass priority) { + rootNode.replace("insert-priority", priority.toString()); + return this; + } + + /** + * Returns the manifest putter. + * + * @return The manifest putter + */ + public ManifestPutter getManifestPutter() { + return ManifestPutter.valueOf(getNodeValue(new String[] { "manifest-putter" }, "simple").toUpperCase()); + } + + /** + * Sets the manifest putter. + * + * @param manifestPutter + * The manifest putter + * @return This configuration + */ + public Configuration setManifestPutter(ManifestPutter manifestPutter) { + rootNode.replace("manifest-putter", manifestPutter.name().toLowerCase()); + return this; + } + } diff --git a/src/de/todesbaum/jsite/main/ConfigurationLocator.java b/src/de/todesbaum/jsite/main/ConfigurationLocator.java new file mode 100644 index 0000000..967f936 --- /dev/null +++ b/src/de/todesbaum/jsite/main/ConfigurationLocator.java @@ -0,0 +1,181 @@ +/* + * jSite - ConfigurationLocator.java - Copyright © 2011–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.main; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * Locator for configuration files in different places. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class ConfigurationLocator { + + /** + * The location of the configuration directory. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + public enum ConfigurationLocation { + + /** The configuration is in the same directory as the JAR file. */ + NEXT_TO_JAR_FILE, + + /** + * The configuration is in the user’s home directory. This is the + * pre-0.9.3 default. + */ + HOME_DIRECTORY, + + /** Custom location. */ + CUSTOM, + + } + + /** The possible configuration locations. */ + private final Map configurationFiles = new HashMap(); + + /** + * Creates a new configuration locator. If this class is loaded from a JAR + * file, {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is added to the list + * of possible configuration file locations. + * {@link ConfigurationLocation#HOME_DIRECTORY} is always added to this + * list, {@link ConfigurationLocation#CUSTOM} has to be enabled by calling + * {@link #setCustomLocation(String)}. + */ + public ConfigurationLocator() { + /* are we executed from a JAR file? */ + String resource = getClass().getResource("/" + getClass().getName().replace(".", "/") + ".class").toString(); + if (resource.startsWith("jar:")) { + String jarFileLocation = resource.substring(9, resource.indexOf(".jar!") + 4); + String jarFileDirectory = new File(jarFileLocation).getParent(); + File configurationFile = new File(jarFileDirectory, "jSite.conf"); + configurationFiles.put(ConfigurationLocation.NEXT_TO_JAR_FILE, configurationFile.getPath()); + } + File homeDirectoryFile = new File(System.getProperty("user.home"), ".jSite/config7"); + configurationFiles.put(ConfigurationLocation.HOME_DIRECTORY, homeDirectoryFile.getPath()); + } + + // + // ACCESSORS + // + + /** + * Sets the location of the custom configuration file. + * + * @param customFile + * The custom location of the configuration file + */ + public void setCustomLocation(String customFile) { + configurationFiles.put(ConfigurationLocation.CUSTOM, customFile); + } + + /** + * Returns whether the given location is valid. Certain locations (such as + * {@link ConfigurationLocation#NEXT_TO_JAR_FILE}) may be invalid in certain + * circumstances (such as the application not being run from a JAR file). A + * location being valid does not imply that a configuration file does exist + * at the given location, use {@link #hasFile(ConfigurationLocation)} to + * check for a configuration file at the desired location. + * + * @param configurationLocation + * The configuration location + * @return {@code true} if the location is valid, {@code false} otherwise + */ + public boolean isValidLocation(ConfigurationLocation configurationLocation) { + return configurationFiles.containsKey(configurationLocation); + } + + /** + * Checks whether a configuration file exists at the given location. + * + * @param configurationLocation + * The configuration location + * @return {@code true} if a configuration file exists at the given + * location, {@code false} otherwise + */ + public boolean hasFile(ConfigurationLocation configurationLocation) { + if (!isValidLocation(configurationLocation)) { + return false; + } + return new File(configurationFiles.get(configurationLocation)).exists(); + } + + /** + * Returns the configuration file for the given location. + * + * @param configurationLocation + * The location to get the file for + * @return The name of the configuration file at the given location, or + * {@code null} if the given location is invalid + */ + public String getFile(ConfigurationLocation configurationLocation) { + return configurationFiles.get(configurationLocation); + } + + // + // ACTIONS + // + + /** + * Finds the preferred location of the configuration file. + * + * @see #findPreferredLocation(ConfigurationLocation) + * @return The preferred location of the configuration file + */ + public ConfigurationLocation findPreferredLocation() { + return findPreferredLocation(ConfigurationLocation.NEXT_TO_JAR_FILE); + } + + /** + * Finds the preferred location of the configuration file. The following + * checks are performed: if a custom configuration location has been defined + * (by calling {@link #setCustomLocation(String)}) + * {@link ConfigurationLocation#CUSTOM} is returned. If the application is + * run from a JAR file and a configuration file is found next to the JAR + * file (i.e. in the same directory), + * {@link ConfigurationLocation#NEXT_TO_JAR_FILE} is returned. If a + * configuration file exists in the user’s home directory, + * {@link ConfigurationLocation#HOME_DIRECTORY} is returned. Otherwise, the + * given {@code defaultLocation} is returned. + * + * @param defaultLocation + * The default location to return if no other configuration file + * is found + * @return The configuration location to load the configuration from + */ + public ConfigurationLocation findPreferredLocation(ConfigurationLocation defaultLocation) { + if (hasFile(ConfigurationLocation.CUSTOM)) { + return ConfigurationLocation.CUSTOM; + } + if (hasFile(ConfigurationLocation.NEXT_TO_JAR_FILE)) { + return ConfigurationLocation.NEXT_TO_JAR_FILE; + } + if (hasFile(ConfigurationLocation.HOME_DIRECTORY)) { + return ConfigurationLocation.HOME_DIRECTORY; + } + if (isValidLocation(defaultLocation)) { + return defaultLocation; + } + return ConfigurationLocation.HOME_DIRECTORY; + } + +} diff --git a/src/de/todesbaum/jsite/main/Main.java b/src/de/todesbaum/jsite/main/Main.java index af921cf..edbf5e4 100644 --- a/src/de/todesbaum/jsite/main/Main.java +++ b/src/de/todesbaum/jsite/main/Main.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006-2009 David Roden + * jSite - Main.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +18,7 @@ package de.todesbaum.jsite.main; +import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; @@ -39,6 +39,7 @@ import javax.swing.Icon; import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButtonMenuItem; @@ -49,10 +50,10 @@ import de.todesbaum.jsite.application.Freenet7Interface; import de.todesbaum.jsite.application.Node; import de.todesbaum.jsite.application.Project; import de.todesbaum.jsite.application.ProjectInserter; -import de.todesbaum.jsite.application.UpdateChecker; -import de.todesbaum.jsite.application.UpdateListener; import de.todesbaum.jsite.application.ProjectInserter.CheckReport; import de.todesbaum.jsite.application.ProjectInserter.Issue; +import de.todesbaum.jsite.application.UpdateChecker; +import de.todesbaum.jsite.application.UpdateListener; import de.todesbaum.jsite.gui.NodeManagerListener; import de.todesbaum.jsite.gui.NodeManagerPage; import de.todesbaum.jsite.gui.PreferencesPage; @@ -61,6 +62,7 @@ import de.todesbaum.jsite.gui.ProjectInsertPage; import de.todesbaum.jsite.gui.ProjectPage; import de.todesbaum.jsite.i18n.I18n; import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; import de.todesbaum.util.image.IconLoader; import de.todesbaum.util.swing.TWizard; import de.todesbaum.util.swing.TWizardPage; @@ -77,7 +79,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, 9, 2); + private static final Version VERSION = new Version(0, 10); /** The configuration. */ private Configuration configuration; @@ -145,6 +147,9 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen /** Mapping from page type to page. */ private final Map pages = new HashMap(); + /** The original location of the configuration file. */ + private ConfigurationLocation originalLocation; + /** * Creates a new core with the default configuration file. */ @@ -159,20 +164,18 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen * The name of the configuration file */ private Main(String configFilename) { + /* collect all possible configuration file locations. */ + ConfigurationLocator configurationLocator = new ConfigurationLocator(); if (configFilename != null) { - configuration = new Configuration(configFilename); - } else { - configuration = new Configuration(); + configurationLocator.setCustomLocation(configFilename); } + + originalLocation = configurationLocator.findPreferredLocation(); + logger.log(Level.CONFIG, "Using configuration from " + originalLocation + "."); + configuration = new Configuration(configurationLocator, originalLocation); + Locale.setDefault(configuration.getLocale()); I18n.setLocale(configuration.getLocale()); - if (!configuration.createLockFile()) { - int option = JOptionPane.showOptionDialog(null, I18n.getMessage("jsite.main.already-running"), "", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new Object[] { I18n.getMessage("jsite.main.already-running.override"), I18n.getMessage("jsite.wizard.quit") }, I18n.getMessage("jsite.wizard.quit")); - if (option != 0) { - throw new IllegalStateException("Lockfile override not active, refusing start."); - } - configuration.removeLockfileOnExit(); - } wizard = new TWizard(); createActions(); wizard.setJMenuBar(createMenuBar()); @@ -356,6 +359,17 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen } /** + * Returns whether a configuration file would be overwritten when calling + * {@link #saveConfiguration()}. + * + * @return {@code true} if {@link #saveConfiguration()} would overwrite an + * existing file, {@code false} otherwise + */ + private boolean isOverwritingConfiguration() { + return configuration.getConfigurationLocator().hasFile(configuration.getConfigurationDirectory()); + } + + /** * Saves the configuration. * * @return true if the configuration could be saved, @@ -444,6 +458,12 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen * Shows a dialog with general preferences. */ private void optionsPreferences() { + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setConfigurationLocation(configuration.getConfigurationDirectory()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasNextToJarConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.NEXT_TO_JAR_FILE)); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasCustomConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.CUSTOM)); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setUseEarlyEncode(configuration.useEarlyEncode()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setPriority(configuration.getPriority()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setManifestPutter(configuration.getManifestPutter()); showPage(PageType.PAGE_PREFERENCES); optionsPreferencesAction.setEnabled(false); wizard.setNextEnabled(true); @@ -536,6 +556,9 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT); String tempDirectory = ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).getTempDirectory(); projectInsertPage.setTempDirectory(tempDirectory); + projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode()); + projectInsertPage.setPriority(configuration.getPriority()); + projectInsertPage.setManifestPutter(configuration.getManifestPutter()); projectInsertPage.startInsert(); nodeMenu.setEnabled(false); optionsPreferencesAction.setEnabled(false); @@ -549,8 +572,13 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen optionsPreferencesAction.setEnabled(true); } } else if ("page.preferences".equals(pageName)) { + PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES); showPage(PageType.PAGE_PROJECTS); optionsPreferencesAction.setEnabled(true); + configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode()); + configuration.setPriority(preferencesPage.getPriority()); + configuration.setManifestPutter(preferencesPage.getManifestPutter()); + configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation()); } } @@ -576,9 +604,23 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen if (((ProjectPage) pages.get(PageType.PAGE_PROJECTS)).wasUriCopied() || ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).wasUriCopied()) { JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.use-clipboard-now")); } - if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { - if (saveConfiguration()) { - System.exit(0); + if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), I18n.getMessage("jsite.quit.question.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { + if (isOverwritingConfiguration() && !originalLocation.equals(configuration.getConfigurationDirectory())) { + int overwriteConfigurationAnswer = JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.quit.overwrite-configuration"), configuration.getConfigurationLocator().getFile(configuration.getConfigurationDirectory())), I18n.getMessage("jsite.quit.overwrite-configuration.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + if (overwriteConfigurationAnswer == JOptionPane.YES_OPTION) { + if (saveConfiguration()) { + System.exit(0); + } + } else if (overwriteConfigurationAnswer == JOptionPane.CANCEL_OPTION) { + return; + } + if (overwriteConfigurationAnswer == JOptionPane.NO_OPTION) { + System.exit(0); + } + } else { + if (saveConfiguration()) { + System.exit(0); + } } if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.config-not-saved"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { System.exit(0); @@ -617,6 +659,25 @@ public class Main implements ActionListener, ListSelectionListener, WizardListen /** * {@inheritDoc} */ + public void nodeSelected(Node node) { + for (Component menuItem : nodeMenu.getMenuComponents()) { + if (menuItem instanceof JMenuItem) { + if (node.equals(((JMenuItem) menuItem).getClientProperty("Node"))) { + ((JMenuItem) menuItem).setSelected(true); + } + } + } + freenetInterface.setNode(node); + selectedNode = node; + } + + // + // INTERFACE ActionListener + // + + /** + * {@inheritDoc} + */ public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source instanceof JRadioButtonMenuItem) { diff --git a/src/de/todesbaum/jsite/main/Version.java b/src/de/todesbaum/jsite/main/Version.java index 2b80c36..977836c 100644 --- a/src/de/todesbaum/jsite/main/Version.java +++ b/src/de/todesbaum/jsite/main/Version.java @@ -1,6 +1,5 @@ /* - * jSite - a tool for uploading websites into Freenet - * Copyright (C) 2006 David Roden + * jSite - Version.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/Client.java b/src/de/todesbaum/util/freenet/fcp2/Client.java index 9376159..ede92d1 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Client.java +++ b/src/de/todesbaum/util/freenet/fcp2/Client.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Client.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +22,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import de.todesbaum.util.io.StreamCopier.ProgressListener; + /** * A Client executes {@link Command}s over a {@link Connection} to a * {@link Node} and delivers resulting {@link Message}s. @@ -113,6 +114,23 @@ public class Client implements ConnectionListener { } /** + * Executes the specified command. This will also clear the queue of + * messages, discarding all messages that resulted from the previous + * command and have not yet been read. + * + * @param command + * The command to execute + * @param progressListener + * The progress listener for payload transfers + * @throws IOException + * if an I/O error occurs + * @see #execute(Command, boolean) + */ + public void execute(Command command, ProgressListener progressListener) throws IOException { + execute(command, true, progressListener); + } + + /** * Executes the specified command and optionally clears the list of * identifiers this clients listens to before starting the command. * @@ -125,6 +143,24 @@ public class Client implements ConnectionListener { * if an I/O error occurs */ public void execute(Command command, boolean removeExistingIdentifiers) throws IOException { + execute(command, removeExistingIdentifiers, null); + } + + /** + * Executes the specified command and optionally clears the list of + * identifiers this clients listens to before starting the command. + * + * @param command + * The command to execute + * @param removeExistingIdentifiers + * If true, the list of identifiers that this + * clients listens to is cleared + * @param progressListener + * The progress listener for payload transfers + * @throws IOException + * if an I/O error occurs + */ + public void execute(Command command, boolean removeExistingIdentifiers, ProgressListener progressListener) throws IOException { synchronized (messageQueue) { messageQueue.clear(); if (removeExistingIdentifiers) { @@ -132,7 +168,7 @@ public class Client implements ConnectionListener { } identifiers.add(command.getIdentifier()); } - connection.execute(command); + connection.execute(command, progressListener); } /** diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientGet.java b/src/de/todesbaum/util/freenet/fcp2/ClientGet.java index d6fb70f..5b340e4 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ClientGet.java +++ b/src/de/todesbaum/util/freenet/fcp2/ClientGet.java @@ -1,6 +1,5 @@ /* - * jSite-remote - ClientGet.java - - * Copyright © 2008 David Roden + * jSite - ClientGet.java - Copyright © 2008–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/de/todesbaum/util/freenet/fcp2/ClientHello.java index 4b9e41d..5983078 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java +++ b/src/de/todesbaum/util/freenet/fcp2/ClientHello.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - ClientHello.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPut.java b/src/de/todesbaum/util/freenet/fcp2/ClientPut.java index acaad49..9993173 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPut.java +++ b/src/de/todesbaum/util/freenet/fcp2/ClientPut.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - ClientPut.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java index 4be7528..d72f2c1 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java +++ b/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - ClientPutComplexDir.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,7 +37,7 @@ import de.todesbaum.util.io.Closer; * @author David Roden <droden@gmail.com> * @version $Id$ */ -public class ClientPutComplexDir extends ClientPutDir { +public class ClientPutComplexDir extends ClientPutDir { /** The file entries of this directory. */ private List fileEntries = new ArrayList(); diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java index b9291a9..386fe1d 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java +++ b/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - ClientPutDir.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,14 +24,72 @@ import java.io.Writer; /** * Abstract base class for all put requests that insert a directory. * + * @param + * The type of the “ClientPutDir” command * @author David Roden <droden@gmail.com> - * @version $Id$ */ -public class ClientPutDir extends ClientPut { +public class 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 <bombe@freenetproject.org> + */ + 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. * @@ -71,6 +128,30 @@ public class 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 @@ -78,6 +159,9 @@ public class ClientPutDir extends ClientPut { super.write(writer); if (defaultName != null) writer.write("DefaultName=" + defaultName + LINEFEED); + if (manifestPutter != null) { + writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED); + } } } diff --git a/src/de/todesbaum/util/freenet/fcp2/Command.java b/src/de/todesbaum/util/freenet/fcp2/Command.java index 9fc9527..63aaa9c 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Command.java +++ b/src/de/todesbaum/util/freenet/fcp2/Command.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Command.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/Connection.java b/src/de/todesbaum/util/freenet/fcp2/Connection.java index 3fd39f6..c4cb669 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Connection.java +++ b/src/de/todesbaum/util/freenet/fcp2/Connection.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Connection.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +33,7 @@ import java.util.List; import de.todesbaum.util.io.Closer; import de.todesbaum.util.io.LineInputStream; import de.todesbaum.util.io.StreamCopier; +import de.todesbaum.util.io.StreamCopier.ProgressListener; import de.todesbaum.util.io.TempFileInputStream; /** @@ -215,34 +215,15 @@ public class Connection { * Disconnects from the node. */ public void disconnect() { - if (nodeWriter != null) { - try { - nodeWriter.close(); - } catch (IOException ioe1) { - } - nodeWriter = null; - } - if (nodeOutputStream != null) { - try { - nodeOutputStream.close(); - } catch (IOException ioe1) { - } - nodeOutputStream = null; - } - if (nodeInputStream != null) { - try { - nodeInputStream.close(); - } catch (IOException ioe1) { - } - nodeInputStream = null; - } - if (nodeSocket != null) { - try { - nodeSocket.close(); - } catch (IOException ioe1) { - } - nodeSocket = null; - } + Closer.close(nodeWriter); + nodeWriter = null; + Closer.close(nodeOutputStream); + nodeOutputStream = null; + Closer.close(nodeInputStream); + nodeInputStream = null; + nodeInputStream = null; + Closer.close(nodeSocket); + nodeSocket = null; synchronized (this) { notify(); } @@ -260,6 +241,22 @@ public class Connection { * if an I/O error occurs */ public synchronized void execute(Command command) throws IllegalStateException, IOException { + execute(command, null); + } + + /** + * Executes the specified command. + * + * @param command + * The command to execute + * @param progressListener + * A progress listener for a payload transfer + * @throws IllegalStateException + * if the connection is not connected + * @throws IOException + * if an I/O error occurs + */ + public synchronized void execute(Command command, ProgressListener progressListener) throws IllegalStateException, IOException { if (nodeSocket == null) { throw new IllegalStateException("connection is not connected"); } @@ -271,7 +268,7 @@ public class Connection { InputStream payloadInputStream = null; try { payloadInputStream = command.getPayload(); - StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength()); + StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength(), progressListener); } finally { Closer.close(payloadInputStream); } @@ -307,6 +304,7 @@ public class Connection { * Main loop of the reader. Lines are read and converted into * {@link Message} objects. */ + @SuppressWarnings("synthetic-access") public void run() { LineInputStream nodeReader = null; try { diff --git a/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java b/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java index 9ce101d..8e55a2d 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java +++ b/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - ConnectionListener.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java index c697c1f..9b12e74 100644 --- a/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java +++ b/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - DirectFileEntry.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java index bb34023..bf036b4 100644 --- a/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java +++ b/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - DiskFileEntry.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/FileEntry.java b/src/de/todesbaum/util/freenet/fcp2/FileEntry.java index 3fcdf8b..7adb11f 100644 --- a/src/de/todesbaum/util/freenet/fcp2/FileEntry.java +++ b/src/de/todesbaum/util/freenet/fcp2/FileEntry.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - FileEntry.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java b/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java index d21b7b9..84e4bf6 100644 --- a/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java +++ b/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - GenerateSSK.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/Message.java b/src/de/todesbaum/util/freenet/fcp2/Message.java index 4810e76..6c934ec 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Message.java +++ b/src/de/todesbaum/util/freenet/fcp2/Message.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Message.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +21,8 @@ package de.todesbaum.util.freenet.fcp2; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; /** * Contains replies sent by the Freenet node. A message always has a name, and diff --git a/src/de/todesbaum/util/freenet/fcp2/Node.java b/src/de/todesbaum/util/freenet/fcp2/Node.java index ee50ca9..d2fa91c 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Node.java +++ b/src/de/todesbaum/util/freenet/fcp2/Node.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Node.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/Persistence.java b/src/de/todesbaum/util/freenet/fcp2/Persistence.java index 6f6cdca..55e73f8 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Persistence.java +++ b/src/de/todesbaum/util/freenet/fcp2/Persistence.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Persistence.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java b/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java index 8a8390e..623bc5b 100644 --- a/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java +++ b/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - PriorityClass.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -90,4 +89,37 @@ public final class PriorityClass { return value; } + // + // STATIC METHODS + // + + /** + * Returns the priority class with the given name, matched case-insensitive. + * + * @param value + * The name of the priority + * @return The priority with the given name, or {@code null} if no priority + * matches the given name + */ + public static PriorityClass valueOf(String value) { + for (PriorityClass priorityClass : new PriorityClass[] { MINIMUM, PREFETCH, BULK, UPDATABLE, SEMI_INTERACTIVE, INTERACTIVE, MAXIMUM }) { + if (priorityClass.getName().equalsIgnoreCase(value)) { + return priorityClass; + } + } + return null; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return name; + } + } diff --git a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java index c4354c6..06a2171 100644 --- a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java +++ b/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - RedirectFileEntry.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/ReturnType.java b/src/de/todesbaum/util/freenet/fcp2/ReturnType.java index 14e2482..6f2ed7a 100644 --- a/src/de/todesbaum/util/freenet/fcp2/ReturnType.java +++ b/src/de/todesbaum/util/freenet/fcp2/ReturnType.java @@ -1,6 +1,5 @@ /* - * jSite-remote - ReturnType.java - - * Copyright © 2008 David Roden + * jSite - ReturnType.java - Copyright © 2008–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/de/todesbaum/util/freenet/fcp2/Verbosity.java index e64540f..e59b6a3 100644 --- a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java +++ b/src/de/todesbaum/util/freenet/fcp2/Verbosity.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - Verbosity.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/image/IconLoader.java b/src/de/todesbaum/util/image/IconLoader.java index d764991..ff6441a 100644 --- a/src/de/todesbaum/util/image/IconLoader.java +++ b/src/de/todesbaum/util/image/IconLoader.java @@ -1,4 +1,6 @@ /* + * jSite - IconLoader.java - Copyright © 2006–2012 David Roden + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later diff --git a/src/de/todesbaum/util/io/Closer.java b/src/de/todesbaum/util/io/Closer.java index ea99081..67f7c57 100644 --- a/src/de/todesbaum/util/io/Closer.java +++ b/src/de/todesbaum/util/io/Closer.java @@ -1,5 +1,5 @@ /* - * todesbaum-lib - Copyright (C) 2006 David Roden + * jSite - Closer.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software diff --git a/src/de/todesbaum/util/io/LineInputStream.java b/src/de/todesbaum/util/io/LineInputStream.java index a49e876..4fe0d07 100644 --- a/src/de/todesbaum/util/io/LineInputStream.java +++ b/src/de/todesbaum/util/io/LineInputStream.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - LineInputStream.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/io/ReplacingOutputStream.java b/src/de/todesbaum/util/io/ReplacingOutputStream.java deleted file mode 100644 index 256714b..0000000 --- a/src/de/todesbaum/util/io/ReplacingOutputStream.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * todesbaum-lib - - * Copyright (C) 2006 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.io; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; - - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class ReplacingOutputStream extends FilterOutputStream { - - private Map replacements = new HashMap(); - private StringBuffer ringBuffer = new StringBuffer(); - - /** - * @param out - */ - public ReplacingOutputStream(OutputStream out) { - super(out); - } - - public void addReplacement(String token, String value) { - replacements.put(token, value); - } - - /** - * {@inheritDoc} - */ - @Override - public void write(int b) throws IOException { - ringBuffer.append((char) b); - Iterator> entries = replacements.entrySet().iterator(); - boolean found = false; - Entry entry = null; - while (!found && entries.hasNext()) { - entry = entries.next(); - if (entry.getKey().startsWith(ringBuffer.toString())) { - found = true; - } - } - if (!found) { - String buffer = ringBuffer.toString(); - for (int index = 0, size = buffer.length(); index < size; index++) { - super.write(buffer.charAt(index)); - } - ringBuffer.setLength(0); - } else { - if (entry.getKey().equals(ringBuffer.toString())) { - String buffer = entry.getValue(); - for (int index = 0, size = buffer.length(); index < size; index++) { - super.write(buffer.charAt(index)); - } - ringBuffer.setLength(0); - } - } - } - -} diff --git a/src/de/todesbaum/util/io/StreamCopier.java b/src/de/todesbaum/util/io/StreamCopier.java index dccd09a..c7d99de 100644 --- a/src/de/todesbaum/util/io/StreamCopier.java +++ b/src/de/todesbaum/util/io/StreamCopier.java @@ -1,4 +1,6 @@ /* + * jSite - StreamCopier.java - Copyright © 2006–2012 David Roden + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later @@ -20,6 +22,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.EventListener; /** * Copies input from an {@link InputStream} to an {@link OutputStream}. @@ -105,6 +108,23 @@ public class StreamCopier { } /** + * Copies the stream data. If the input stream is depleted before the + * requested number of bytes have been read an {@link EOFException} is + * thrown. + * + * @param progressListener + * The progress listener (may be {@code null}) + * @throws EOFException + * if the input stream is depleted before the requested number + * of bytes has been read + * @throws IOException + * if an I/O error occurs + */ + public void copy(ProgressListener progressListener) throws EOFException, IOException { + copy(inputStream, outputStream, length, bufferSize, progressListener); + } + + /** * Copies length bytes from the inputStream to * the outputStream. * @@ -123,6 +143,25 @@ public class StreamCopier { /** * Copies length bytes from the inputStream to + * the outputStream. + * + * @param inputStream + * The input stream to read from + * @param outputStream + * The output stream to write to + * @param length + * The number of bytes to copy + * @param progressListener + * The progress listener (may be {@code null}) + * @throws IOException + * if an I/O exception occurs + */ + public static void copy(InputStream inputStream, OutputStream outputStream, long length, ProgressListener progressListener) throws IOException { + copy(inputStream, outputStream, length, BUFFER_SIZE, progressListener); + } + + /** + * Copies length bytes from the inputStream to * the outputStream using a buffer with the specified size * * @param inputStream @@ -137,6 +176,27 @@ public class StreamCopier { * if an I/O exception occurs */ public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) throws IOException { + copy(inputStream, outputStream, length, bufferSize, null); + } + + /** + * Copies length bytes from the inputStream to + * the outputStream using a buffer with the specified size + * + * @param inputStream + * The input stream to read from + * @param outputStream + * The output stream to write to + * @param length + * The number of bytes to copy + * @param bufferSize + * The size of the copy buffer + * @param progressListener + * The progress listener (may be {@code null}) + * @throws IOException + * if an I/O exception occurs + */ + public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize, ProgressListener progressListener) throws IOException { long remaining = length; byte[] buffer = new byte[bufferSize]; while (remaining > 0) { @@ -146,7 +206,30 @@ public class StreamCopier { } outputStream.write(buffer, 0, read); remaining -= read; + if (progressListener != null) { + progressListener.onProgress(length - remaining, length); + } } } + /** + * Interface for objects that want to be notified about the progress of a + * {@link StreamCopier#copy()} operation. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + public static interface ProgressListener extends EventListener { + + /** + * Notifiies a listener that a copy process made some progress. + * + * @param copied + * The number of bytes that have already been copied + * @param length + * The total number of bytes that will be copied + */ + public void onProgress(long copied, long length); + + } + } diff --git a/src/de/todesbaum/util/io/TeeOutputStream.java b/src/de/todesbaum/util/io/TeeOutputStream.java new file mode 100644 index 0000000..78860dc --- /dev/null +++ b/src/de/todesbaum/util/io/TeeOutputStream.java @@ -0,0 +1,125 @@ +/* + * jSite - TeeOutputStream.java - Copyright © 2010 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.todesbaum.util.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@link OutputStream} that sends all data it receives to multiple other output + * streams. If an error occurs during a {@link #write(int)} to one of the + * underlying output streams no guarantees are made about how much data is sent + * to each stream, i.e. there is no good way to recover from such an error. + * + * @author David ‘Bombe’ Roden + */ +public class TeeOutputStream extends OutputStream { + + /** The output streams. */ + private final OutputStream[] outputStreams; + + /** + * Creates a new tee output stream that sends all to all given output + * streams. + * + * @param outputStreams + * The output streams to send all data to + */ + public TeeOutputStream(OutputStream... outputStreams) { + this.outputStreams = outputStreams; + } + + /** + * {@inheritDoc} + *

+ * An effort is made to close all output streams. If multiple exceptions + * occur, only the first exception is thrown after all output streams have + * been tried to close. + */ + @Override + public void close() throws IOException { + IOException occuredException = null; + for (OutputStream outputStream : outputStreams) { + try { + outputStream.flush(); + } catch (IOException ioe1) { + if (occuredException == null) { + occuredException = ioe1; + } + } + } + if (occuredException != null) { + throw occuredException; + } + } + + /** + * {@inheritDoc} + *

+ * An effort is made to flush all output streams. If multiple exceptions + * occur, only the first exception is thrown after all output streams have + * been tried to flush. + */ + @Override + public void flush() throws IOException { + IOException occuredException = null; + for (OutputStream outputStream : outputStreams) { + try { + outputStream.flush(); + } catch (IOException ioe1) { + if (occuredException == null) { + occuredException = ioe1; + } + } + } + if (occuredException != null) { + throw occuredException; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] buffer) throws IOException { + for (OutputStream outputStream : outputStreams) { + outputStream.write(buffer); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + for (OutputStream outputStream : outputStreams) { + outputStream.write(buffer, offset, length); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void write(int data) throws IOException { + for (OutputStream outputStream : outputStreams) { + outputStream.write(data); + } + } + +} diff --git a/src/de/todesbaum/util/io/TempFileInputStream.java b/src/de/todesbaum/util/io/TempFileInputStream.java index 5ebeb9b..43e2047 100644 --- a/src/de/todesbaum/util/io/TempFileInputStream.java +++ b/src/de/todesbaum/util/io/TempFileInputStream.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - TempFileInputStream.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/swing/SortedListModel.java b/src/de/todesbaum/util/swing/SortedListModel.java index 683368c..39a5ce2 100644 --- a/src/de/todesbaum/util/swing/SortedListModel.java +++ b/src/de/todesbaum/util/swing/SortedListModel.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - SortedListModel.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/swing/TLabel.java b/src/de/todesbaum/util/swing/TLabel.java index 6515627..0393f5c 100644 --- a/src/de/todesbaum/util/swing/TLabel.java +++ b/src/de/todesbaum/util/swing/TLabel.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - TLabel.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/swing/TWizard.java b/src/de/todesbaum/util/swing/TWizard.java index 0ccffbc..464ac69 100644 --- a/src/de/todesbaum/util/swing/TWizard.java +++ b/src/de/todesbaum/util/swing/TWizard.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - TWizard.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/swing/TWizardPage.java b/src/de/todesbaum/util/swing/TWizardPage.java index be51b7e..a10b475 100644 --- a/src/de/todesbaum/util/swing/TWizardPage.java +++ b/src/de/todesbaum/util/swing/TWizardPage.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - TWizardPage.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/swing/WizardListener.java b/src/de/todesbaum/util/swing/WizardListener.java index f0ab6b0..987075d 100644 --- a/src/de/todesbaum/util/swing/WizardListener.java +++ b/src/de/todesbaum/util/swing/WizardListener.java @@ -1,6 +1,5 @@ /* - * todesbaum-lib - - * Copyright (C) 2006 David Roden + * jSite - WizardListener.java - Copyright © 2006–2012 David Roden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/de/todesbaum/util/xml/SimpleXML.java b/src/de/todesbaum/util/xml/SimpleXML.java index b41d600..8351581 100644 --- a/src/de/todesbaum/util/xml/SimpleXML.java +++ b/src/de/todesbaum/util/xml/SimpleXML.java @@ -1,4 +1,6 @@ /* + * jSite - SimpleXML.java - Copyright © 2006–2012 David Roden + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/src/de/todesbaum/util/xml/XML.java b/src/de/todesbaum/util/xml/XML.java index 28f8298..8b6b259 100644 --- a/src/de/todesbaum/util/xml/XML.java +++ b/src/de/todesbaum/util/xml/XML.java @@ -1,4 +1,6 @@ /* + * jSite - XML.java - Copyright © 2006–2012 David Roden + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later